ParsingBuffer.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 #include "squid.h"
10 #include "sbuf/Stream.h"
11 #include "SquidMath.h"
12 #include "store/ParsingBuffer.h"
13 
14 #include <iostream>
15 
16 // Several Store::ParsingBuffer() methods use assert() because the corresponding
17 // failure means there is a good chance that somebody have already read from (or
18 // written to) the wrong memory location. Since this buffer is used for storing
19 // HTTP response bytes, such failures may corrupt traffic. No Assure() handling
20 // code can safely recover from such failures.
21 
23  readerSuppliedMemory_(initialSpace)
24 {
25 }
26 
28 const char *
30 {
31  return extraMemory_ ? extraMemory_->rawContent() : readerSuppliedMemory_.data;
32 }
33 
34 size_t
36 {
37  return extraMemory_ ? (extraMemory_->length() + extraMemory_->spaceSize()) : readerSuppliedMemory_.length;
38 }
39 
40 size_t
42 {
43  return extraMemory_ ? extraMemory_->length() : readerSuppliedMemoryContentSize_;
44 }
45 
46 void
47 Store::ParsingBuffer::appended(const char * const newBytes, const size_t newByteCount)
48 {
49  // a positive newByteCount guarantees that, after the first assertion below
50  // succeeds, the second assertion will not increment a nil memory() pointer
51  if (!newByteCount)
52  return;
53 
54  // these checks order guarantees that memory() is not nil in the second assertion
55  assert(newByteCount <= spaceSize()); // the new bytes end in our space
56  assert(memory() + contentSize() == newBytes); // the new bytes start in our space
57  // and now we know that newBytes is not nil either
58 
59  if (extraMemory_)
60  extraMemory_->rawAppendFinish(newBytes, newByteCount);
61  else
62  readerSuppliedMemoryContentSize_ = *IncreaseSum(readerSuppliedMemoryContentSize_, newByteCount);
63 
64  assert(contentSize() <= capacity()); // paranoid
65 }
66 
67 void
68 Store::ParsingBuffer::consume(const size_t parsedBytes)
69 {
70  Assure(contentSize() >= parsedBytes); // more conservative than extraMemory_->consume()
71  if (extraMemory_) {
72  extraMemory_->consume(parsedBytes);
73  } else {
74  readerSuppliedMemoryContentSize_ -= parsedBytes;
75  if (parsedBytes && readerSuppliedMemoryContentSize_)
76  memmove(readerSuppliedMemory_.data, memory() + parsedBytes, readerSuppliedMemoryContentSize_);
77  }
78 }
79 
82 {
83  const auto size = spaceSize();
84  const auto start = extraMemory_ ?
85  extraMemory_->rawAppendStart(size) :
86  (readerSuppliedMemory_.data + readerSuppliedMemoryContentSize_);
87  return StoreIOBuffer(spaceSize(), 0, start);
88 }
89 
91 Store::ParsingBuffer::makeSpace(const size_t pageSize)
92 {
93  growSpace(pageSize);
94  auto result = space();
95  Assure(result.length >= pageSize);
96  result.length = pageSize;
97  return result;
98 }
99 
102 {
103  // This const_cast is a StoreIOBuffer API limitation: That class does not
104  // support a "constant content view", even though it is used as such a view.
105  return StoreIOBuffer(contentSize(), 0, const_cast<char*>(memory()));
106 }
107 
109 void
110 Store::ParsingBuffer::growSpace(const size_t minimumSpaceSize)
111 {
112  const auto capacityIncreaseAttempt = IncreaseSum(contentSize(), minimumSpaceSize);
113  if (!capacityIncreaseAttempt)
114  throw TextException(ToSBuf("no support for a single memory block of ", contentSize(), '+', minimumSpaceSize, " bytes"), Here());
115  const auto newCapacity = *capacityIncreaseAttempt;
116 
117  if (newCapacity <= capacity())
118  return; // already have enough space; no reallocation is needed
119 
120  debugs(90, 7, "growing to provide " << minimumSpaceSize << " in " << *this);
121 
122  if (extraMemory_) {
123  extraMemory_->reserveCapacity(newCapacity);
124  } else {
125  SBuf newStorage;
126  newStorage.reserveCapacity(newCapacity);
127  newStorage.append(readerSuppliedMemory_.data, readerSuppliedMemoryContentSize_);
128  extraMemory_ = std::move(newStorage);
129  }
130  Assure(spaceSize() >= minimumSpaceSize);
131 }
132 
133 SBuf
135 {
136  return extraMemory_ ? *extraMemory_ : SBuf(content().data, content().length);
137 }
138 
139 size_t
141 {
142  if (extraMemory_)
143  return extraMemory_->spaceSize();
144 
145  assert(readerSuppliedMemoryContentSize_ <= readerSuppliedMemory_.length);
146  return readerSuppliedMemory_.length - readerSuppliedMemoryContentSize_;
147 }
148 
151 void
153 {
154  *makeSpace(1).data = 0;
155 }
156 
159 {
160  const auto bytesToPack = contentSize();
161  // until our callers do not have to work around legacy code expectations
162  Assure(bytesToPack);
163 
164  // if we accumulated more bytes at some point, any extra metadata should
165  // have been consume()d by now, allowing readerSuppliedMemory_.data reuse
166  Assure(bytesToPack <= readerSuppliedMemory_.length);
167 
168  auto result = readerSuppliedMemory_;
169  result.length = bytesToPack;
170  Assure(result.data);
171 
172  if (!extraMemory_) {
173  // no accumulated bytes copying because they are in readerSuppliedMemory_
174  debugs(90, 7, "quickly exporting " << result.length << " bytes via " << readerSuppliedMemory_);
175  } else {
176  debugs(90, 7, "slowly exporting " << result.length << " bytes from " << extraMemory_->id << " back into " << readerSuppliedMemory_);
177  memmove(result.data, extraMemory_->rawContent(), result.length);
178  }
179 
180  return result;
181 }
182 
183 void
184 Store::ParsingBuffer::print(std::ostream &os) const
185 {
186  os << "size=" << contentSize();
187 
188  if (extraMemory_) {
189  os << " capacity=" << capacity();
190  os << " extra=" << extraMemory_->id;
191  }
192 
193  // report readerSuppliedMemory_ (if any) even if we are no longer using it
194  // for content storage; it affects packBack() and related parsing logic
195  if (readerSuppliedMemory_.length)
196  os << ' ' << readerSuppliedMemory_;
197 }
198 
#define Here()
source code location of the caller
Definition: Here.h:15
StoreIOBuffer makeSpace(size_t pageSize)
Definition: SBuf.h:93
size_t capacity() const
the maximum number of bytes we can store without allocating more space
int size
Definition: ModDevPoll.cc:69
void consume(size_t)
get rid of previously appended() prefix of a given size
void appended(const char *, size_t)
remember the new bytes received into the previously provided space()
void print(std::ostream &) const
summarizes object state (for debugging)
#define assert(EX)
Definition: assert.h:17
#define Assure(condition)
Definition: Assure.h:35
void growSpace(size_t)
makes sure we have the requested number of bytes, allocates enough memory if needed
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
StoreIOBuffer content() const
void reserveCapacity(size_type minCapacity)
Definition: SBuf.cc:105
an std::runtime_error with thrower location info
Definition: TextException.h:20
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
SBuf toSBuf() const
export content() into SBuf, avoiding content copying when possible
std::optional< S > IncreaseSum(const S s, const T t)
argument pack expansion termination for IncreaseSum<S, T, Args...>()
Definition: SquidMath.h:144
StoreIOBuffer packBack()
const char * memory() const
a read-only content start (or nil for some zero-size buffers)
size_t spaceSize() const
the number of bytes in the space() buffer
ParsingBuffer()=default
creates buffer without any space or content
size_t contentSize() const
the total number of append()ed bytes that were not consume()d
StoreIOBuffer space()
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192

 

Introduction

Documentation

Support

Miscellaneous