1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /**************************************************************************
25   UIOBuffer.cc
26 
27 **************************************************************************/
28 #include "tscore/ink_defs.h"
29 #include "P_EventSystem.h"
30 
31 //
32 // General Buffer Allocator
33 //
34 inkcoreapi Allocator ioBufAllocator[DEFAULT_BUFFER_SIZES];
35 inkcoreapi ClassAllocator<MIOBuffer> ioAllocator("ioAllocator", DEFAULT_BUFFER_NUMBER);
36 inkcoreapi ClassAllocator<IOBufferData> ioDataAllocator("ioDataAllocator", DEFAULT_BUFFER_NUMBER);
37 inkcoreapi ClassAllocator<IOBufferBlock> ioBlockAllocator("ioBlockAllocator", DEFAULT_BUFFER_NUMBER);
38 int64_t default_large_iobuffer_size = DEFAULT_LARGE_BUFFER_SIZE;
39 int64_t default_small_iobuffer_size = DEFAULT_SMALL_BUFFER_SIZE;
40 int64_t max_iobuffer_size           = DEFAULT_BUFFER_SIZES - 1;
41 
42 //
43 // Initialization
44 //
45 void
init_buffer_allocators(int iobuffer_advice)46 init_buffer_allocators(int iobuffer_advice)
47 {
48   for (int i = 0; i < DEFAULT_BUFFER_SIZES; i++) {
49     int64_t s = DEFAULT_BUFFER_BASE_SIZE * ((static_cast<int64_t>(1)) << i);
50     int64_t a = DEFAULT_BUFFER_ALIGNMENT;
51     int n     = i <= default_large_iobuffer_size ? DEFAULT_BUFFER_NUMBER : DEFAULT_HUGE_BUFFER_NUMBER;
52     if (s < a) {
53       a = s;
54     }
55 
56     auto name = new char[64];
57     snprintf(name, 64, "ioBufAllocator[%d]", i);
58     ioBufAllocator[i].re_init(name, s, n, a, iobuffer_advice);
59   }
60 }
61 
62 //
63 // MIOBuffer
64 //
65 int64_t
write(const void * abuf,int64_t alen)66 MIOBuffer::write(const void *abuf, int64_t alen)
67 {
68   const char *buf = static_cast<const char *>(abuf);
69   int64_t len     = alen;
70   while (len) {
71     if (!_writer) {
72       add_block();
73     }
74     int64_t f = _writer->write_avail();
75     f         = f < len ? f : len;
76     if (f > 0) {
77       ::memcpy(_writer->end(), buf, f);
78       _writer->fill(f);
79       buf += f;
80       len -= f;
81     }
82     if (len) {
83       if (!_writer->next) {
84         add_block();
85       } else {
86         _writer = _writer->next;
87       }
88     }
89   }
90   return alen;
91 }
92 
93 int64_t
write(IOBufferReader * r,int64_t len,int64_t offset)94 MIOBuffer::write(IOBufferReader *r, int64_t len, int64_t offset)
95 {
96   return this->write(r->block.get(), len, offset + r->start_offset);
97 }
98 
99 int64_t
write(IOBufferChain const * chain,int64_t len,int64_t offset)100 MIOBuffer::write(IOBufferChain const *chain, int64_t len, int64_t offset)
101 {
102   return this->write(chain->head(), std::min(len, chain->length()), offset);
103 }
104 
105 int64_t
write(IOBufferBlock const * b,int64_t alen,int64_t offset)106 MIOBuffer::write(IOBufferBlock const *b, int64_t alen, int64_t offset)
107 {
108   int64_t len = alen;
109 
110   while (b && len > 0) {
111     int64_t max_bytes = b->read_avail();
112     max_bytes -= offset;
113     if (max_bytes <= 0) {
114       offset = -max_bytes;
115       b      = b->next.get();
116       continue;
117     }
118     int64_t bytes;
119     if (len >= max_bytes) {
120       bytes = max_bytes;
121     } else {
122       bytes = len;
123     }
124     IOBufferBlock *bb = b->clone();
125     bb->_start += offset;
126     bb->_buf_end = bb->_end = bb->_start + bytes;
127     append_block(bb);
128     offset = 0;
129     len -= bytes;
130     b = b->next.get();
131   }
132 
133   return alen - len;
134 }
135 
136 bool
is_max_read_avail_more_than(int64_t size)137 MIOBuffer::is_max_read_avail_more_than(int64_t size)
138 {
139   bool no_reader = true;
140   for (auto &reader : this->readers) {
141     if (reader.allocated()) {
142       if (reader.is_read_avail_more_than(size)) {
143         return true;
144       }
145       no_reader = false;
146     }
147   }
148 
149   if (no_reader && this->_writer) {
150     return (this->_writer->read_avail() > size);
151   }
152 
153   return false;
154 }
155 
156 //
157 // IOBufferReader
158 //
159 int64_t
read(void * ab,int64_t len)160 IOBufferReader::read(void *ab, int64_t len)
161 {
162   char *b       = static_cast<char *>(ab);
163   int64_t n     = len;
164   int64_t l     = block_read_avail();
165   int64_t bytes = 0;
166 
167   while (n && l) {
168     if (n < l) {
169       l = n;
170     }
171     ::memcpy(b, start(), l);
172     consume(l);
173     b += l;
174     n -= l;
175     bytes += l;
176     l = block_read_avail();
177   }
178   return bytes;
179 }
180 
181 // TODO: I don't think this method is used anywhere, so perhaps get rid of it ?
182 int64_t
memchr(char c,int64_t len,int64_t offset)183 IOBufferReader::memchr(char c, int64_t len, int64_t offset)
184 {
185   IOBufferBlock *b = block.get();
186   offset += start_offset;
187   int64_t o = offset;
188 
189   while (b && len) {
190     int64_t max_bytes = b->read_avail();
191     max_bytes -= offset;
192     if (max_bytes <= 0) {
193       offset = -max_bytes;
194       b      = b->next.get();
195       continue;
196     }
197     int64_t bytes;
198     if (len < 0 || len >= max_bytes) {
199       bytes = max_bytes;
200     } else {
201       bytes = len;
202     }
203     char *s = b->start() + offset;
204     char *p = static_cast<char *>(::memchr(s, c, bytes));
205     if (p) {
206       return static_cast<int64_t>(o - start_offset + p - s);
207     }
208     o += bytes;
209     len -= bytes;
210     b      = b->next.get();
211     offset = 0;
212   }
213 
214   return -1;
215 }
216 
217 char *
memcpy(void * ap,int64_t len,int64_t offset)218 IOBufferReader::memcpy(void *ap, int64_t len, int64_t offset)
219 {
220   char *p          = static_cast<char *>(ap);
221   IOBufferBlock *b = block.get();
222   offset += start_offset;
223 
224   while (b && len) {
225     int64_t max_bytes = b->read_avail();
226     max_bytes -= offset;
227     if (max_bytes <= 0) {
228       offset = -max_bytes;
229       b      = b->next.get();
230       continue;
231     }
232     int64_t bytes;
233     if (len < 0 || len >= max_bytes) {
234       bytes = max_bytes;
235     } else {
236       bytes = len;
237     }
238     ::memcpy(p, b->start() + offset, bytes);
239     p += bytes;
240     len -= bytes;
241     b      = b->next.get();
242     offset = 0;
243   }
244 
245   return p;
246 }
247 
248 //
249 // IOBufferChain
250 //
251 int64_t
write(IOBufferBlock * blocks,int64_t length,int64_t offset)252 IOBufferChain::write(IOBufferBlock *blocks, int64_t length, int64_t offset)
253 {
254   int64_t n = length;
255 
256   while (blocks && n > 0) {
257     int64_t block_bytes = blocks->read_avail();
258     if (block_bytes <= offset) { // skip the entire block
259       offset -= block_bytes;
260     } else {
261       int64_t bytes     = std::min(n, block_bytes - offset);
262       IOBufferBlock *bb = blocks->clone();
263       if (offset) {
264         bb->consume(offset);
265         block_bytes -= offset; // bytes really available to use.
266         offset = 0;
267       }
268       if (block_bytes > n) {
269         bb->_end -= (block_bytes - n);
270       }
271       // Attach the cloned block since its data will be kept.
272       this->append(bb);
273       n -= bytes;
274     }
275     blocks = blocks->next.get();
276   }
277 
278   length -= n; // actual bytes written to chain.
279   _len += length;
280   return length;
281 }
282 
283 int64_t
write(IOBufferData * data,int64_t length,int64_t offset)284 IOBufferChain::write(IOBufferData *data, int64_t length, int64_t offset)
285 {
286   int64_t zret     = 0;
287   IOBufferBlock *b = new_IOBufferBlock();
288 
289   if (length < 0) {
290     length = 0;
291   }
292 
293   b->set(data, length, offset);
294   this->append(b);
295 
296   zret = b->read_avail();
297   _len += zret;
298   return zret;
299 }
300 
301 void
append(IOBufferBlock * block)302 IOBufferChain::append(IOBufferBlock *block)
303 {
304   if (nullptr == _tail) {
305     _head = block;
306     _tail = block;
307   } else {
308     _tail->next = block;
309     _tail       = block;
310   }
311 }
312 
313 int64_t
consume(int64_t size)314 IOBufferChain::consume(int64_t size)
315 {
316   int64_t zret = 0;
317   int64_t bytes;
318   size = std::min(size, _len);
319 
320   while (_head != nullptr && size > 0 && (bytes = _head->read_avail()) > 0) {
321     if (size >= bytes) {
322       _head = _head->next;
323       zret += bytes;
324       size -= bytes;
325     } else {
326       _head->consume(size);
327       zret += size;
328       size = 0;
329     }
330   }
331   _len -= zret;
332   if (_head == nullptr || _len == 0) {
333     _head = nullptr, _tail = nullptr, _len = 0;
334   }
335   return zret;
336 }
337