]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_digest.cc
Supply ALE with HttpReply before checking http_reply_access (#398)
[thirdparty/squid.git] / src / store_digest.cc
CommitLineData
8638fc66 1/*
f6e9a3ee 2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
e25c139f 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.
8638fc66 7 */
8
bbc27441
AJ
9/* DEBUG: section 71 Store Digest Manager */
10
6168bccd 11/*
12 * TODO: We probably do not track all the cases when
13 * storeDigestNoteStoreReady() must be called; this may prevent
14 * storeDigestRebuild/write schedule to be activated
15 */
16
582c2af2
FC
17#include "squid.h"
18#include "Debug.h"
a553a5a3 19#include "event.h"
582c2af2 20#include "globals.h"
8822ebee 21#include "mgr/Registration.h"
35a28a37 22#include "store_digest.h"
d6fd3381 23
b814e8d4
FC
24#if USE_CACHE_DIGESTS
25#include "CacheDigest.h"
528b2c61 26#include "HttpReply.h"
582c2af2 27#include "HttpRequest.h"
308e60be 28#include "internal.h"
528b2c61 29#include "MemObject.h"
aa839030 30#include "PeerDigest.h"
c6f15d40 31#include "refresh.h"
4d5904f7 32#include "SquidConfig.h"
985c86bc 33#include "SquidTime.h"
582c2af2 34#include "Store.h"
c8f4eac4 35#include "StoreSearch.h"
ed6e9fb9 36#include "util.h"
528b2c61 37
074d6a40 38#include <cmath>
582c2af2 39
d6fd3381 40/*
41 * local types
42 */
12784378 43
c8f4eac4 44class StoreDigestState
62e76326 45{
c8f4eac4 46public:
12784378 47 StoreDigestCBlock cblock;
b56b37cf
AJ
48 int rebuild_lock = 0; ///< bucket number
49 StoreEntry * rewrite_lock = nullptr; ///< points to store entry with the digest
c8f4eac4 50 StoreSearchPointer theSearch;
b56b37cf
AJ
51 int rewrite_offset = 0;
52 int rebuild_count = 0;
53 int rewrite_count = 0;
c8f4eac4 54};
12784378 55
b56b37cf
AJ
56class StoreDigestStats
57{
58public:
59 int del_count = 0; /* #store entries deleted from store_digest */
60 int del_lost_count = 0; /* #store entries not found in store_digest on delete */
61 int add_count = 0; /* #store entries accepted to store_digest */
62 int add_coll_count = 0; /* #accepted entries that collided with existing ones */
63 int rej_count = 0; /* #store entries not accepted to store_digest */
64 int rej_coll_count = 0; /* #not accepted entries that collided with existing ones */
65};
6168bccd 66
462f66d2 67/* local vars */
12784378 68static StoreDigestState sd_state;
6168bccd 69static StoreDigestStats sd_stats;
12784378 70
71/* local prototypes */
6168bccd 72static void storeDigestRebuildStart(void *datanotused);
d6fd3381 73static void storeDigestRebuildResume(void);
74static void storeDigestRebuildFinish(void);
12784378 75static void storeDigestRebuildStep(void *datanotused);
d6fd3381 76static void storeDigestRewriteStart(void *);
77static void storeDigestRewriteResume(void);
b644367b 78static void storeDigestRewriteFinish(StoreEntry * e);
d6f51e3c 79static EVH storeDigestSwapOutStep;
b644367b 80static void storeDigestCBlockSwapOut(StoreEntry * e);
d6fd3381 81static void storeDigestAdd(const StoreEntry *);
12784378 82
831e953c
AJ
83/// calculates digest capacity
84static uint64_t
85storeDigestCalcCap()
86{
87 /*
88 * To-Do: Bloom proved that the optimal filter utilization is 50% (half of
89 * the bits are off). However, we do not have a formula to calculate the
90 * number of _entries_ we want to pre-allocate for.
91 */
92 const uint64_t hi_cap = Store::Root().maxSize() / Config.Store.avgObjectSize;
93 const uint64_t lo_cap = 1 + Store::Root().currentSize() / Config.Store.avgObjectSize;
94 const uint64_t e_count = StoreEntry::inUseCount();
95 uint64_t cap = e_count ? e_count : hi_cap;
96 debugs(71, 2, "have: " << e_count << ", want " << cap <<
97 " entries; limits: [" << lo_cap << ", " << hi_cap << "]");
98
99 if (cap < lo_cap)
100 cap = lo_cap;
101
102 /* do not enforce hi_cap limit, average-based estimation may be wrong
103 *if (cap > hi_cap)
104 * cap = hi_cap;
105 */
106
107 // Bug 4534: we still have to set an upper-limit at some reasonable value though.
108 // this matches cacheDigestCalcMaskSize doing (cap*bpe)+7 < INT_MAX
109 const uint64_t absolute_max = (INT_MAX -8) / Config.digest.bits_per_entry;
110 if (cap > absolute_max) {
6008010b
AJ
111 static time_t last_loud = 0;
112 if (last_loud < squid_curtime - 86400) {
113 debugs(71, DBG_IMPORTANT, "WARNING: Cache Digest cannot store " << cap << " entries. Limiting to " << absolute_max);
114 last_loud = squid_curtime;
115 } else {
116 debugs(71, 3, "WARNING: Cache Digest cannot store " << cap << " entries. Limiting to " << absolute_max);
117 }
831e953c
AJ
118 cap = absolute_max;
119 }
120
121 return cap;
122}
123#endif /* USE_CACHE_DIGESTS */
12784378 124
8638fc66 125void
d6fd3381 126storeDigestInit(void)
8638fc66 127{
8f0386af 128 Mgr::RegisterAction("store_digest", "Store Digest", storeDigestReport, 0, 1);
d120ed12 129
6cfa8966 130#if USE_CACHE_DIGESTS
7e3ce7b9 131 if (!Config.onoff.digest_generation) {
62e76326 132 store_digest = NULL;
bf8fe701 133 debugs(71, 3, "Local cache digest generation disabled");
62e76326 134 return;
7e3ce7b9 135 }
62e76326 136
831e953c 137 const uint64_t cap = storeDigestCalcCap();
e04fc9d3 138 store_digest = new CacheDigest(cap, Config.digest.bits_per_entry);
e0236918 139 debugs(71, DBG_IMPORTANT, "Local cache digest enabled; rebuild/rewrite every " <<
bf8fe701 140 (int) Config.digest.rebuild_period << "/" <<
141 (int) Config.digest.rewrite_period << " sec");
142
b56b37cf 143 sd_state = StoreDigestState();
d6fd3381 144#else
145 store_digest = NULL;
bf8fe701 146 debugs(71, 3, "Local cache digest is 'off'");
d6fd3381 147#endif
8638fc66 148}
149
6168bccd 150/* called when store_rebuild completes */
8638fc66 151void
d6fd3381 152storeDigestNoteStoreReady(void)
12784378 153{
d6fd3381 154#if USE_CACHE_DIGESTS
62e76326 155
7e3ce7b9 156 if (Config.onoff.digest_generation) {
62e76326 157 storeDigestRebuildStart(NULL);
158 storeDigestRewriteStart(NULL);
7e3ce7b9 159 }
62e76326 160
d6fd3381 161#endif
162}
163
35a28a37 164//TODO: this seems to be dead code. Is it needed?
d6fd3381 165void
166storeDigestDel(const StoreEntry * entry)
167{
168#if USE_CACHE_DIGESTS
62e76326 169
7e3ce7b9 170 if (!Config.onoff.digest_generation) {
62e76326 171 return;
7e3ce7b9 172 }
62e76326 173
d6fd3381 174 assert(entry && store_digest);
bf8fe701 175 debugs(71, 6, "storeDigestDel: checking entry, key: " << entry->getMD5Text());
62e76326 176
d46a87a8 177 if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) {
6fc4e508 178 if (!store_digest->contains(static_cast<const cache_key *>(entry->key))) {
5db6bf73 179 ++sd_stats.del_lost_count;
bf8fe701 180 debugs(71, 6, "storeDigestDel: lost entry, key: " << entry->getMD5Text() << " url: " << entry->url() );
62e76326 181 } else {
5db6bf73 182 ++sd_stats.del_count;
fbba122c 183 store_digest->remove(static_cast<const cache_key *>(entry->key));
bf8fe701 184 debugs(71, 6, "storeDigestDel: deled entry, key: " << entry->getMD5Text());
62e76326 185 }
d6fd3381 186 }
35a28a37 187#endif //USE_CACHE_DIGESTS
12784378 188}
189
12784378 190void
d6fd3381 191storeDigestReport(StoreEntry * e)
192{
193#if USE_CACHE_DIGESTS
62e76326 194
7e3ce7b9 195 if (!Config.onoff.digest_generation) {
62e76326 196 return;
7e3ce7b9 197 }
62e76326 198
d6fd3381 199 if (store_digest) {
0e3b8c9f
AJ
200 static const SBuf label("store");
201 cacheDigestReport(store_digest, label, e);
62e76326 202 storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n",
203 sd_stats.add_count,
204 sd_stats.rej_count,
205 xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count),
206 sd_stats.del_count);
207 storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n",
208 xpercent(sd_stats.add_coll_count, sd_stats.add_count),
209 xpercent(sd_stats.rej_coll_count, sd_stats.rej_count));
d6fd3381 210 } else {
62e76326 211 storeAppendPrintf(e, "store digest: disabled.\n");
d6fd3381 212 }
62e76326 213
35a28a37 214#endif //USE_CACHE_DIGESTS
d6fd3381 215}
216
217/*
218 * LOCAL FUNCTIONS
219 */
220
221#if USE_CACHE_DIGESTS
222
c68e9c6b 223/* should we digest this entry? used by storeDigestAdd() */
224static int
225storeDigestAddable(const StoreEntry * e)
226{
227 /* add some stats! XXX */
228
bf8fe701 229 debugs(71, 6, "storeDigestAddable: checking entry, key: " << e->getMD5Text());
c68e9c6b 230
3900307b 231 /* check various entry flags (mimics StoreEntry::checkCachable XXX) */
62e76326 232
c68e9c6b 233 if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
bf8fe701 234 debugs(71, 6, "storeDigestAddable: NO: private key");
62e76326 235 return 0;
c68e9c6b 236 }
62e76326 237
c68e9c6b 238 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
bf8fe701 239 debugs(71, 6, "storeDigestAddable: NO: negative cached");
62e76326 240 return 0;
c68e9c6b 241 }
62e76326 242
c68e9c6b 243 if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
bf8fe701 244 debugs(71, 6, "storeDigestAddable: NO: release requested");
62e76326 245 return 0;
c68e9c6b 246 }
62e76326 247
c68e9c6b 248 if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
bf8fe701 249 debugs(71, 6, "storeDigestAddable: NO: wrong content-length");
62e76326 250 return 0;
c68e9c6b 251 }
62e76326 252
c68e9c6b 253 /* do not digest huge objects */
47f6e231 254 if (e->swap_file_sz > (uint64_t )Config.Store.maxObjectSize) {
bf8fe701 255 debugs(71, 6, "storeDigestAddable: NO: too big");
62e76326 256 return 0;
c68e9c6b 257 }
62e76326 258
c68e9c6b 259 /* still here? check staleness */
260 /* Note: We should use the time of the next rebuild, not (cur_time+period) */
7e3ce7b9 261 if (refreshCheckDigest(e, Config.digest.rebuild_period)) {
4a7a3d56 262 debugs(71, 6, "storeDigestAdd: entry expires within " << Config.digest.rebuild_period << " secs, ignoring");
62e76326 263 return 0;
c68e9c6b 264 }
62e76326 265
17a80fc2 266 /*
267 * idea: how about also skipping very fresh (thus, potentially
268 * unstable) entries? Should be configurable through
269 * cd_refresh_pattern, of course.
270 */
271 /*
272 * idea: skip objects that are going to be purged before the next
273 * update.
274 */
c68e9c6b 275 return 1;
276}
277
d6fd3381 278static void
6168bccd 279storeDigestAdd(const StoreEntry * entry)
280{
6168bccd 281 assert(entry && store_digest);
c68e9c6b 282
283 if (storeDigestAddable(entry)) {
5db6bf73 284 ++sd_stats.add_count;
62e76326 285
6fc4e508 286 if (store_digest->contains(static_cast<const cache_key *>(entry->key)))
5db6bf73 287 ++sd_stats.add_coll_count;
62e76326 288
fbba122c 289 store_digest->add(static_cast<const cache_key *>(entry->key));
62e76326 290
bf8fe701 291 debugs(71, 6, "storeDigestAdd: added entry, key: " << entry->getMD5Text());
6168bccd 292 } else {
5db6bf73 293 ++sd_stats.rej_count;
62e76326 294
6fc4e508 295 if (store_digest->contains(static_cast<const cache_key *>(entry->key)))
5db6bf73 296 ++sd_stats.rej_coll_count;
6168bccd 297 }
12784378 298}
299
12784378 300/* rebuilds digest from scratch */
301static void
6168bccd 302storeDigestRebuildStart(void *datanotused)
8638fc66 303{
304 assert(store_digest);
12784378 305 /* prevent overlapping if rebuild schedule is too tight */
62e76326 306
12784378 307 if (sd_state.rebuild_lock) {
e0236918 308 debugs(71, DBG_IMPORTANT, "storeDigestRebuildStart: overlap detected, consider increasing rebuild period");
62e76326 309 return;
12784378 310 }
62e76326 311
12784378 312 sd_state.rebuild_lock = 1;
bf8fe701 313 debugs(71, 2, "storeDigestRebuildStart: rebuild #" << sd_state.rebuild_count + 1);
62e76326 314
6168bccd 315 if (sd_state.rewrite_lock) {
bf8fe701 316 debugs(71, 2, "storeDigestRebuildStart: waiting for Rewrite to finish.");
62e76326 317 return;
6168bccd 318 }
62e76326 319
6168bccd 320 storeDigestRebuildResume();
321}
322
831e953c
AJ
323/// \returns true if we actually resized the digest
324static bool
325storeDigestResize()
326{
327 const uint64_t cap = storeDigestCalcCap();
328 assert(store_digest);
3faf5197
AJ
329 uint64_t diff;
330 if (cap > store_digest->capacity)
331 diff = cap - store_digest->capacity;
332 else
333 diff = store_digest->capacity - cap;
831e953c
AJ
334 debugs(71, 2, store_digest->capacity << " -> " << cap << "; change: " <<
335 diff << " (" << xpercentInt(diff, store_digest->capacity) << "%)" );
336 /* avoid minor adjustments */
337
338 if (diff <= store_digest->capacity / 10) {
339 debugs(71, 2, "small change, will not resize.");
340 return false;
341 } else {
342 debugs(71, 2, "big change, resizing.");
343 store_digest->updateCapacity(cap);
344 }
345 return true;
346}
347
6168bccd 348/* called be Rewrite to push Rebuild forward */
349static void
d6fd3381 350storeDigestRebuildResume(void)
6168bccd 351{
352 assert(sd_state.rebuild_lock);
353 assert(!sd_state.rewrite_lock);
2745fea5 354 sd_state.theSearch = Store::Root().search();
304b267e 355 /* resize or clear */
62e76326 356
304b267e 357 if (!storeDigestResize())
28faff32 358 store_digest->clear(); /* not clean()! */
62e76326 359
b56b37cf 360 sd_stats = StoreDigestStats();
62e76326 361
c43f5247 362 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 363}
364
365/* finishes swap out sequence for the digest; schedules next rebuild */
366static void
d6fd3381 367storeDigestRebuildFinish(void)
12784378 368{
369 assert(sd_state.rebuild_lock);
370 sd_state.rebuild_lock = 0;
5db6bf73 371 ++sd_state.rebuild_count;
bf8fe701 372 debugs(71, 2, "storeDigestRebuildFinish: done.");
7e3ce7b9 373 eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, (double)
62e76326 374 Config.digest.rebuild_period, 1);
6168bccd 375 /* resume pending Rewrite if any */
62e76326 376
12784378 377 if (sd_state.rewrite_lock)
62e76326 378 storeDigestRewriteResume();
12784378 379}
380
381/* recalculate a few hash buckets per invocation; schedules next step */
382static void
383storeDigestRebuildStep(void *datanotused)
384{
c8f4eac4 385 /* TODO: call Store::Root().size() to determine this.. */
386 int count = Config.Store.objectsPerBucket * (int) ceil((double) store_hash_buckets *
387 (double) Config.digest.rebuild_chunk_percentage / 100.0);
12784378 388 assert(sd_state.rebuild_lock);
62e76326 389
bf8fe701 390 debugs(71, 3, "storeDigestRebuildStep: buckets: " << store_hash_buckets << " entries to check: " << count);
62e76326 391
c8f4eac4 392 while (count-- && !sd_state.theSearch->isDone() && sd_state.theSearch->next())
393 storeDigestAdd(sd_state.theSearch->currentItem());
62e76326 394
12784378 395 /* are we done ? */
c8f4eac4 396 if (sd_state.theSearch->isDone())
62e76326 397 storeDigestRebuildFinish();
12784378 398 else
62e76326 399 eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1);
12784378 400}
401
12784378 402/* starts swap out sequence for the digest */
403static void
6168bccd 404storeDigestRewriteStart(void *datanotused)
12784378 405{
12784378 406 assert(store_digest);
407 /* prevent overlapping if rewrite schedule is too tight */
62e76326 408
12784378 409 if (sd_state.rewrite_lock) {
e0236918 410 debugs(71, DBG_IMPORTANT, "storeDigestRewrite: overlap detected, consider increasing rewrite period");
62e76326 411 return;
12784378 412 }
62e76326 413
bf8fe701 414 debugs(71, 2, "storeDigestRewrite: start rewrite #" << sd_state.rewrite_count + 1);
8babada0
AJ
415
416 const char *url = internalLocalUri("/squid-internal-periodic/", SBuf(StoreDigestFileName));
417 const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initCacheDigest);
418 auto req = HttpRequest::FromUrl(url, mx);
419
420 RequestFlags flags;
e857372a 421 flags.cachable = true;
8babada0
AJ
422
423 StoreEntry *e = storeCreateEntry(url, url, flags, Http::METHOD_GET);
28c60158 424 assert(e);
aa839030 425 sd_state.rewrite_lock = e;
bf8fe701 426 debugs(71, 3, "storeDigestRewrite: url: " << url << " key: " << e->getMD5Text());
8babada0 427 e->mem_obj->request = req;
62e76326 428
8babada0 429 /* wait for rebuild (if any) to finish */
6168bccd 430 if (sd_state.rebuild_lock) {
bf8fe701 431 debugs(71, 2, "storeDigestRewriteStart: waiting for rebuild to finish.");
62e76326 432 return;
6168bccd 433 }
62e76326 434
6168bccd 435 storeDigestRewriteResume();
436}
437
438static void
d6fd3381 439storeDigestRewriteResume(void)
6168bccd 440{
28c60158 441 StoreEntry *e;
6168bccd 442
443 assert(sd_state.rewrite_lock);
444 assert(!sd_state.rebuild_lock);
aa839030 445 e = sd_state.rewrite_lock;
12784378 446 sd_state.rewrite_offset = 0;
d46a87a8 447 EBIT_SET(e->flags, ENTRY_SPECIAL);
462f66d2 448 /* setting public key will purge old digest entry if any */
d88e3c49 449 e->setPublicKey();
462f66d2 450 /* fake reply */
06a5ae20 451 HttpReply *rep = new HttpReply;
955394ce 452 rep->setHeaders(Http::scOkay, "Cache Digest OK",
11992b6f
AJ
453 "application/cache-digest", (store_digest->mask_size + sizeof(sd_state.cblock)),
454 squid_curtime, (squid_curtime + Config.digest.rewrite_period) );
26ac0430 455 debugs(71, 3, "storeDigestRewrite: entry expires on " << rep->expires <<
bf8fe701 456 " (" << std::showpos << (int) (rep->expires - squid_curtime) << ")");
3900307b 457 e->buffer();
db237875 458 e->replaceHttpReply(rep);
12784378 459 storeDigestCBlockSwapOut(e);
3900307b 460 e->flush();
aa839030 461 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1, false);
12784378 462}
463
464/* finishes swap out sequence for the digest; schedules next rewrite */
465static void
b644367b 466storeDigestRewriteFinish(StoreEntry * e)
12784378 467{
aa839030 468 assert(e == sd_state.rewrite_lock);
528b2c61 469 e->complete();
3900307b 470 e->timestampsSet();
26ac0430 471 debugs(71, 2, "storeDigestRewriteFinish: digest expires at " << e->expires <<
bf8fe701 472 " (" << std::showpos << (int) (e->expires - squid_curtime) << ")");
6168bccd 473 /* is this the write order? @?@ */
528b2c61 474 e->mem_obj->unlinkRequest();
acc5dc4c 475 e->unlock("storeDigestRewriteFinish");
feefc1d9 476 sd_state.rewrite_lock = NULL;
5db6bf73 477 ++sd_state.rewrite_count;
7e3ce7b9 478 eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, (double)
62e76326 479 Config.digest.rewrite_period, 1);
6168bccd 480 /* resume pending Rebuild if any */
62e76326 481
6168bccd 482 if (sd_state.rebuild_lock)
62e76326 483 storeDigestRebuildResume();
12784378 484}
485
486/* swaps out one digest "chunk" per invocation; schedules next swap out */
487static void
52040193 488storeDigestSwapOutStep(void *data)
12784378 489{
aa839030 490 StoreEntry *e = static_cast<StoreEntry *>(data);
7e3ce7b9 491 int chunk_size = Config.digest.swapout_chunk_size;
aa839030 492 assert(e == sd_state.rewrite_lock);
12784378 493 assert(e);
12784378 494 /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */
62e76326 495
831e953c 496 if (static_cast<uint32_t>(sd_state.rewrite_offset + chunk_size) > store_digest->mask_size)
62e76326 497 chunk_size = store_digest->mask_size - sd_state.rewrite_offset;
498
3900307b 499 e->append(store_digest->mask + sd_state.rewrite_offset, chunk_size);
62e76326 500
e4049756 501 debugs(71, 3, "storeDigestSwapOutStep: size: " << store_digest->mask_size <<
502 " offset: " << sd_state.rewrite_offset << " chunk: " <<
503 chunk_size << " bytes");
62e76326 504
12784378 505 sd_state.rewrite_offset += chunk_size;
62e76326 506
12784378 507 /* are we done ? */
831e953c 508 if (static_cast<uint32_t>(sd_state.rewrite_offset) >= store_digest->mask_size)
62e76326 509 storeDigestRewriteFinish(e);
12784378 510 else
aa839030 511 eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, data, 0.0, 1, false);
12784378 512}
513
514static void
b644367b 515storeDigestCBlockSwapOut(StoreEntry * e)
12784378 516{
12784378 517 memset(&sd_state.cblock, 0, sizeof(sd_state.cblock));
462f66d2 518 sd_state.cblock.ver.current = htons(CacheDigestVer.current);
519 sd_state.cblock.ver.required = htons(CacheDigestVer.required);
520 sd_state.cblock.capacity = htonl(store_digest->capacity);
521 sd_state.cblock.count = htonl(store_digest->count);
522 sd_state.cblock.del_count = htonl(store_digest->del_count);
523 sd_state.cblock.mask_size = htonl(store_digest->mask_size);
831e953c 524 sd_state.cblock.bits_per_entry = Config.digest.bits_per_entry;
6168bccd 525 sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount;
3900307b 526 e->append((char *) &sd_state.cblock, sizeof(sd_state.cblock));
8638fc66 527}
528
d6fd3381 529#endif /* USE_CACHE_DIGESTS */
f53969cc 530