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 #ifdef WRITE_AND_TRANSFER
112 /*
113  * Same functionality as write but for the one small difference.
114  * The space available in the last block is taken from the original
115  * and this space becomes available to the copy.
116  *
117  */
118 int64_t
119 MIOBuffer::write_and_transfer_left_over_space(IOBufferReader *r, int64_t alen, int64_t offset)
120 {
121   int64_t rval = write(r, alen, offset);
122   // reset the end markers of the original so that it cannot
123   // make use of the space in the current block
124   if (r->mbuf->_writer)
125     r->mbuf->_writer->_buf_end = r->mbuf->_writer->_end;
126   // reset the end marker of the clone so that it can make
127   // use of the space in the current block
128   if (_writer) {
129     _writer->_buf_end = _writer->data->data() + _writer->block_size();
130   }
131   return rval;
132 }
133 
134 #endif
135 
136 int64_t
137 MIOBuffer::write(IOBufferReader *r, int64_t len, int64_t offset)
138 {
139   return this->write(r->block.get(), len, offset + r->start_offset);
140 }
141 
142 int64_t
143 MIOBuffer::write(IOBufferChain const *chain, int64_t len, int64_t offset)
144 {
145   return this->write(chain->head(), std::min(len, chain->length()), offset);
146 }
147 
148 int64_t
149 MIOBuffer::write(IOBufferBlock const *b, int64_t alen, int64_t offset)
150 {
151   int64_t len = alen;
152 
153   while (b && len > 0) {
154     int64_t max_bytes = b->read_avail();
155     max_bytes -= offset;
156     if (max_bytes <= 0) {
157       offset = -max_bytes;
158       b      = b->next.get();
159       continue;
160     }
161     int64_t bytes;
162     if (len < 0 || len >= max_bytes) {
163       bytes = max_bytes;
164     } else {
165       bytes = len;
166     }
167     IOBufferBlock *bb = b->clone();
168     bb->_start += offset;
169     bb->_buf_end = bb->_end = bb->_start + bytes;
170     append_block(bb);
171     offset = 0;
172     len -= bytes;
173     b = b->next.get();
174   }
175 
176   return alen - len;
177 }
178 
179 int64_t
180 MIOBuffer::puts(char *s, int64_t len)
181 {
182   char *pc = end();
183   char *pb = s;
184   while (pc < buf_end()) {
185     if (len-- <= 0) {
186       return -1;
187     }
188     if (!*pb || *pb == '\n') {
189       int64_t n = static_cast<int64_t>(pb - s);
190       memcpy(end(), s, n + 1); // Up to and including '\n'
191       end()[n + 1] = 0;
192       fill(n + 1);
193       return n + 1;
194     }
195     pc++;
196     pb++;
197   }
198   return 0;
199 }
200 
201 int64_t
202 IOBufferReader::read(void *ab, int64_t len)
203 {
204   char *b       = static_cast<char *>(ab);
205   int64_t n     = len;
206   int64_t l     = block_read_avail();
207   int64_t bytes = 0;
208 
209   while (n && l) {
210     if (n < l) {
211       l = n;
212     }
213     ::memcpy(b, start(), l);
214     consume(l);
215     b += l;
216     n -= l;
217     bytes += l;
218     l = block_read_avail();
219   }
220   return bytes;
221 }
222 
223 // TODO: I don't think this method is used anywhere, so perhaps get rid of it ?
224 int64_t
225 IOBufferReader::memchr(char c, int64_t len, int64_t offset)
226 {
227   IOBufferBlock *b = block.get();
228   offset += start_offset;
229   int64_t o = offset;
230 
231   while (b && len) {
232     int64_t max_bytes = b->read_avail();
233     max_bytes -= offset;
234     if (max_bytes <= 0) {
235       offset = -max_bytes;
236       b      = b->next.get();
237       continue;
238     }
239     int64_t bytes;
240     if (len < 0 || len >= max_bytes) {
241       bytes = max_bytes;
242     } else {
243       bytes = len;
244     }
245     char *s = b->start() + offset;
246     char *p = static_cast<char *>(::memchr(s, c, bytes));
247     if (p) {
248       return static_cast<int64_t>(o - start_offset + p - s);
249     }
250     o += bytes;
251     len -= bytes;
252     b      = b->next.get();
253     offset = 0;
254   }
255 
256   return -1;
257 }
258 
259 char *
260 IOBufferReader::memcpy(const void *ap, int64_t len, int64_t offset)
261 {
262   char *p          = (char *)ap;
263   IOBufferBlock *b = block.get();
264   offset += start_offset;
265 
266   while (b && len) {
267     int64_t max_bytes = b->read_avail();
268     max_bytes -= offset;
269     if (max_bytes <= 0) {
270       offset = -max_bytes;
271       b      = b->next.get();
272       continue;
273     }
274     int64_t bytes;
275     if (len < 0 || len >= max_bytes) {
276       bytes = max_bytes;
277     } else {
278       bytes = len;
279     }
280     ::memcpy(p, b->start() + offset, bytes);
281     p += bytes;
282     len -= bytes;
283     b      = b->next.get();
284     offset = 0;
285   }
286 
287   return p;
288 }
289 
290 int64_t
291 IOBufferChain::write(IOBufferBlock *blocks, int64_t length, int64_t offset)
292 {
293   int64_t n = length;
294 
295   while (blocks && n > 0) {
296     int64_t block_bytes = blocks->read_avail();
297     if (block_bytes <= offset) { // skip the entire block
298       offset -= block_bytes;
299     } else {
300       int64_t bytes     = std::min(n, block_bytes - offset);
301       IOBufferBlock *bb = blocks->clone();
302       if (offset) {
303         bb->consume(offset);
304         block_bytes -= offset; // bytes really available to use.
305         offset = 0;
306       }
307       if (block_bytes > n)
308         bb->_end -= (block_bytes - n);
309       // Attach the cloned block since its data will be kept.
310       this->append(bb);
311       n -= bytes;
312     }
313     blocks = blocks->next.get();
314   }
315 
316   length -= n; // actual bytes written to chain.
317   _len += length;
318   return length;
319 }
320 
321 int64_t
322 IOBufferChain::write(IOBufferData *data, int64_t length, int64_t offset)
323 {
324   int64_t zret     = 0;
325   IOBufferBlock *b = new_IOBufferBlock();
326 
327   if (length < 0)
328     length = 0;
329 
330   b->set(data, length, offset);
331   this->append(b);
332 
333   zret = b->read_avail();
334   _len += zret;
335   return zret;
336 }
337 
338 void
339 IOBufferChain::append(IOBufferBlock *block)
340 {
341   if (nullptr == _tail) {
342     _head = block;
343     _tail = block;
344   } else {
345     _tail->next = block;
346     _tail       = block;
347   }
348 }
349 
350 int64_t
351 IOBufferChain::consume(int64_t size)
352 {
353   int64_t zret = 0;
354   int64_t bytes;
355   size = std::min(size, _len);
356 
357   while (_head != nullptr && size > 0 && (bytes = _head->read_avail()) > 0) {
358     if (size >= bytes) {
359       _head = _head->next;
360       zret += bytes;
361       size -= bytes;
362     } else {
363       _head->consume(size);
364       zret += size;
365       size = 0;
366     }
367   }
368   _len -= zret;
369   if (_head == nullptr || _len == 0) {
370     _head = nullptr, _tail = nullptr, _len = 0;
371   }
372   return zret;
373 }
374 
375 //-- MIOBufferWriter
376 MIOBufferWriter &
377 MIOBufferWriter::write(const void *data_, size_t length)
378 {
379   const char *data = static_cast<const char *>(data_);
380 
381   while (length) {
382     IOBufferBlock *iobbPtr = _miob->first_write_block();
383 
384     if (!iobbPtr) {
385       addBlock();
386 
387       iobbPtr = _miob->first_write_block();
388 
389       ink_assert(iobbPtr);
390     }
391 
392     size_t writeSize = iobbPtr->write_avail();
393 
394     if (length < writeSize) {
395       writeSize = length;
396     }
397 
398     std::memcpy(iobbPtr->end(), data, writeSize);
399     iobbPtr->fill(writeSize);
400 
401     data += writeSize;
402     length -= writeSize;
403 
404     _numWritten += writeSize;
405   }
406 
407   return *this;
408 }
409 
410 std::ostream &
411 MIOBufferWriter::operator>>(std::ostream &stream) const
412 {
413   IOBufferReader *r = _miob->alloc_reader();
414   if (r) {
415     IOBufferBlock *b;
416     while (nullptr != (b = r->get_current_block())) {
417       auto n = b->read_avail();
418       stream.write(b->start(), n);
419       r->consume(n);
420     }
421     _miob->dealloc_reader(r);
422   }
423   return stream;
424 }
425 
426 ssize_t
427 MIOBufferWriter::operator>>(int fd) const
428 {
429   ssize_t zret           = 0;
430   IOBufferReader *reader = _miob->alloc_reader();
431   if (reader) {
432     IOBufferBlock *b;
433     while (nullptr != (b = reader->get_current_block())) {
434       auto n = b->read_avail();
435       auto r = ::write(fd, b->start(), n);
436       if (r <= 0) {
437         break;
438       } else {
439         reader->consume(r);
440         zret += r;
441       }
442     }
443     _miob->dealloc_reader(reader);
444   }
445   return zret;
446 }
447