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 "I_MIOBufferWriter.h"
30 #include "P_EventSystem.h"
31 
32 //
33 // General Buffer Allocator
34 //
35 inkcoreapi Allocator ioBufAllocator[DEFAULT_BUFFER_SIZES];
36 inkcoreapi ClassAllocator<MIOBuffer> ioAllocator("ioAllocator", DEFAULT_BUFFER_NUMBER);
37 inkcoreapi ClassAllocator<IOBufferData> ioDataAllocator("ioDataAllocator", DEFAULT_BUFFER_NUMBER);
38 inkcoreapi ClassAllocator<IOBufferBlock> ioBlockAllocator("ioBlockAllocator", DEFAULT_BUFFER_NUMBER);
39 int64_t default_large_iobuffer_size = DEFAULT_LARGE_BUFFER_SIZE;
40 int64_t default_small_iobuffer_size = DEFAULT_SMALL_BUFFER_SIZE;
41 int64_t max_iobuffer_size           = DEFAULT_BUFFER_SIZES - 1;
42 
43 //
44 // Initialization
45 //
46 void
47 init_buffer_allocators(int iobuffer_advice)
48 {
49   for (int i = 0; i < DEFAULT_BUFFER_SIZES; i++) {
50     int64_t s = DEFAULT_BUFFER_BASE_SIZE * ((static_cast<int64_t>(1)) << i);
51     int64_t a = DEFAULT_BUFFER_ALIGNMENT;
52     int n     = i <= default_large_iobuffer_size ? DEFAULT_BUFFER_NUMBER : DEFAULT_HUGE_BUFFER_NUMBER;
53     if (s < a) {
54       a = s;
55     }
56 
57     auto name = new char[64];
58     snprintf(name, 64, "ioBufAllocator[%d]", i);
59     ioBufAllocator[i].re_init(name, s, n, a, iobuffer_advice);
60   }
61 }
62 
63 int64_t
64 MIOBuffer::remove_append(IOBufferReader *r)
65 {
66   int64_t l = 0;
67   while (r->block) {
68     Ptr<IOBufferBlock> b = r->block;
69     r->block             = r->block->next;
70     b->_start += r->start_offset;
71     if (b->start() >= b->end()) {
72       r->start_offset = -r->start_offset;
73       continue;
74     }
75     r->start_offset = 0;
76     l += b->read_avail();
77     append_block(b.get());
78   }
79   r->mbuf->_writer = nullptr;
80   return l;
81 }
82 
83 int64_t
84 MIOBuffer::write(const void *abuf, int64_t alen)
85 {
86   const char *buf = static_cast<const char *>(abuf);
87   int64_t len     = alen;
88   while (len) {
89     if (!_writer) {
90       add_block();
91     }
92     int64_t f = _writer->write_avail();
93     f         = f < len ? f : len;
94     if (f > 0) {
95       ::memcpy(_writer->end(), buf, f);
96       _writer->fill(f);
97       buf += f;
98       len -= f;
99     }
100     if (len) {
101       if (!_writer->next) {
102         add_block();
103       } else {
104         _writer = _writer->next;
105       }
106     }
107   }
108   return alen;
109 }
110 
111 int64_t
112 MIOBuffer::write(IOBufferReader *r, int64_t len, int64_t offset)
113 {
114   return this->write(r->block.get(), len, offset + r->start_offset);
115 }
116 
117 int64_t
118 MIOBuffer::write(IOBufferChain const *chain, int64_t len, int64_t offset)
119 {
120   return this->write(chain->head(), std::min(len, chain->length()), offset);
121 }
122 
123 int64_t
124 MIOBuffer::write(IOBufferBlock const *b, int64_t alen, int64_t offset)
125 {
126   int64_t len = alen;
127 
128   while (b && len > 0) {
129     int64_t max_bytes = b->read_avail();
130     max_bytes -= offset;
131     if (max_bytes <= 0) {
132       offset = -max_bytes;
133       b      = b->next.get();
134       continue;
135     }
136     int64_t bytes;
137     if (len < 0 || len >= max_bytes) {
138       bytes = max_bytes;
139     } else {
140       bytes = len;
141     }
142     IOBufferBlock *bb = b->clone();
143     bb->_start += offset;
144     bb->_buf_end = bb->_end = bb->_start + bytes;
145     append_block(bb);
146     offset = 0;
147     len -= bytes;
148     b = b->next.get();
149   }
150 
151   return alen - len;
152 }
153 
154 int64_t
155 MIOBuffer::puts(char *s, int64_t len)
156 {
157   char *pc = end();
158   char *pb = s;
159   while (pc < buf_end()) {
160     if (len-- <= 0) {
161       return -1;
162     }
163     if (!*pb || *pb == '\n') {
164       int64_t n = static_cast<int64_t>(pb - s);
165       memcpy(end(), s, n + 1); // Up to and including '\n'
166       end()[n + 1] = 0;
167       fill(n + 1);
168       return n + 1;
169     }
170     pc++;
171     pb++;
172   }
173   return 0;
174 }
175 
176 int64_t
177 IOBufferReader::read(void *ab, int64_t len)
178 {
179   char *b       = static_cast<char *>(ab);
180   int64_t n     = len;
181   int64_t l     = block_read_avail();
182   int64_t bytes = 0;
183 
184   while (n && l) {
185     if (n < l) {
186       l = n;
187     }
188     ::memcpy(b, start(), l);
189     consume(l);
190     b += l;
191     n -= l;
192     bytes += l;
193     l = block_read_avail();
194   }
195   return bytes;
196 }
197 
198 // TODO: I don't think this method is used anywhere, so perhaps get rid of it ?
199 int64_t
200 IOBufferReader::memchr(char c, int64_t len, int64_t offset)
201 {
202   IOBufferBlock *b = block.get();
203   offset += start_offset;
204   int64_t o = offset;
205 
206   while (b && len) {
207     int64_t max_bytes = b->read_avail();
208     max_bytes -= offset;
209     if (max_bytes <= 0) {
210       offset = -max_bytes;
211       b      = b->next.get();
212       continue;
213     }
214     int64_t bytes;
215     if (len < 0 || len >= max_bytes) {
216       bytes = max_bytes;
217     } else {
218       bytes = len;
219     }
220     char *s = b->start() + offset;
221     char *p = static_cast<char *>(::memchr(s, c, bytes));
222     if (p) {
223       return static_cast<int64_t>(o - start_offset + p - s);
224     }
225     o += bytes;
226     len -= bytes;
227     b      = b->next.get();
228     offset = 0;
229   }
230 
231   return -1;
232 }
233 
234 char *
235 IOBufferReader::memcpy(void *ap, int64_t len, int64_t offset)
236 {
237   char *p          = static_cast<char *>(ap);
238   IOBufferBlock *b = block.get();
239   offset += start_offset;
240 
241   while (b && len) {
242     int64_t max_bytes = b->read_avail();
243     max_bytes -= offset;
244     if (max_bytes <= 0) {
245       offset = -max_bytes;
246       b      = b->next.get();
247       continue;
248     }
249     int64_t bytes;
250     if (len < 0 || len >= max_bytes) {
251       bytes = max_bytes;
252     } else {
253       bytes = len;
254     }
255     ::memcpy(p, b->start() + offset, bytes);
256     p += bytes;
257     len -= bytes;
258     b      = b->next.get();
259     offset = 0;
260   }
261 
262   return p;
263 }
264 
265 int64_t
266 IOBufferChain::write(IOBufferBlock *blocks, int64_t length, int64_t offset)
267 {
268   int64_t n = length;
269 
270   while (blocks && n > 0) {
271     int64_t block_bytes = blocks->read_avail();
272     if (block_bytes <= offset) { // skip the entire block
273       offset -= block_bytes;
274     } else {
275       int64_t bytes     = std::min(n, block_bytes - offset);
276       IOBufferBlock *bb = blocks->clone();
277       if (offset) {
278         bb->consume(offset);
279         block_bytes -= offset; // bytes really available to use.
280         offset = 0;
281       }
282       if (block_bytes > n) {
283         bb->_end -= (block_bytes - n);
284       }
285       // Attach the cloned block since its data will be kept.
286       this->append(bb);
287       n -= bytes;
288     }
289     blocks = blocks->next.get();
290   }
291 
292   length -= n; // actual bytes written to chain.
293   _len += length;
294   return length;
295 }
296 
297 int64_t
298 IOBufferChain::write(IOBufferData *data, int64_t length, int64_t offset)
299 {
300   int64_t zret     = 0;
301   IOBufferBlock *b = new_IOBufferBlock();
302 
303   if (length < 0) {
304     length = 0;
305   }
306 
307   b->set(data, length, offset);
308   this->append(b);
309 
310   zret = b->read_avail();
311   _len += zret;
312   return zret;
313 }
314 
315 void
316 IOBufferChain::append(IOBufferBlock *block)
317 {
318   if (nullptr == _tail) {
319     _head = block;
320     _tail = block;
321   } else {
322     _tail->next = block;
323     _tail       = block;
324   }
325 }
326 
327 int64_t
328 IOBufferChain::consume(int64_t size)
329 {
330   int64_t zret = 0;
331   int64_t bytes;
332   size = std::min(size, _len);
333 
334   while (_head != nullptr && size > 0 && (bytes = _head->read_avail()) > 0) {
335     if (size >= bytes) {
336       _head = _head->next;
337       zret += bytes;
338       size -= bytes;
339     } else {
340       _head->consume(size);
341       zret += size;
342       size = 0;
343     }
344   }
345   _len -= zret;
346   if (_head == nullptr || _len == 0) {
347     _head = nullptr, _tail = nullptr, _len = 0;
348   }
349   return zret;
350 }
351 
352 //-- MIOBufferWriter
353 MIOBufferWriter &
354 MIOBufferWriter::write(const void *data_, size_t length)
355 {
356   const char *data = static_cast<const char *>(data_);
357 
358   while (length) {
359     IOBufferBlock *iobbPtr = _miob->first_write_block();
360 
361     if (!iobbPtr) {
362       addBlock();
363 
364       iobbPtr = _miob->first_write_block();
365 
366       ink_assert(iobbPtr);
367     }
368 
369     size_t writeSize = iobbPtr->write_avail();
370 
371     if (length < writeSize) {
372       writeSize = length;
373     }
374 
375     std::memcpy(iobbPtr->end(), data, writeSize);
376     iobbPtr->fill(writeSize);
377 
378     data += writeSize;
379     length -= writeSize;
380 
381     _numWritten += writeSize;
382   }
383 
384   return *this;
385 }
386 
387 std::ostream &
388 MIOBufferWriter::operator>>(std::ostream &stream) const
389 {
390   IOBufferReader *r = _miob->alloc_reader();
391   if (r) {
392     IOBufferBlock *b;
393     while (nullptr != (b = r->get_current_block())) {
394       auto n = b->read_avail();
395       stream.write(b->start(), n);
396       r->consume(n);
397     }
398     _miob->dealloc_reader(r);
399   }
400   return stream;
401 }
402 
403 ssize_t
404 MIOBufferWriter::operator>>(int fd) const
405 {
406   ssize_t zret           = 0;
407   IOBufferReader *reader = _miob->alloc_reader();
408   if (reader) {
409     IOBufferBlock *b;
410     while (nullptr != (b = reader->get_current_block())) {
411       auto n = b->read_avail();
412       auto r = ::write(fd, b->start(), n);
413       if (r <= 0) {
414         break;
415       } else {
416         reader->consume(r);
417         zret += r;
418       }
419     }
420     _miob->dealloc_reader(reader);
421   }
422   return zret;
423 }
424