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(void *ap, int64_t len, int64_t offset)
261 {
262   char *p          = static_cast<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       }
310       // Attach the cloned block since its data will be kept.
311       this->append(bb);
312       n -= bytes;
313     }
314     blocks = blocks->next.get();
315   }
316 
317   length -= n; // actual bytes written to chain.
318   _len += length;
319   return length;
320 }
321 
322 int64_t
323 IOBufferChain::write(IOBufferData *data, int64_t length, int64_t offset)
324 {
325   int64_t zret     = 0;
326   IOBufferBlock *b = new_IOBufferBlock();
327 
328   if (length < 0) {
329     length = 0;
330   }
331 
332   b->set(data, length, offset);
333   this->append(b);
334 
335   zret = b->read_avail();
336   _len += zret;
337   return zret;
338 }
339 
340 void
341 IOBufferChain::append(IOBufferBlock *block)
342 {
343   if (nullptr == _tail) {
344     _head = block;
345     _tail = block;
346   } else {
347     _tail->next = block;
348     _tail       = block;
349   }
350 }
351 
352 int64_t
353 IOBufferChain::consume(int64_t size)
354 {
355   int64_t zret = 0;
356   int64_t bytes;
357   size = std::min(size, _len);
358 
359   while (_head != nullptr && size > 0 && (bytes = _head->read_avail()) > 0) {
360     if (size >= bytes) {
361       _head = _head->next;
362       zret += bytes;
363       size -= bytes;
364     } else {
365       _head->consume(size);
366       zret += size;
367       size = 0;
368     }
369   }
370   _len -= zret;
371   if (_head == nullptr || _len == 0) {
372     _head = nullptr, _tail = nullptr, _len = 0;
373   }
374   return zret;
375 }
376 
377 //-- MIOBufferWriter
378 MIOBufferWriter &
379 MIOBufferWriter::write(const void *data_, size_t length)
380 {
381   const char *data = static_cast<const char *>(data_);
382 
383   while (length) {
384     IOBufferBlock *iobbPtr = _miob->first_write_block();
385 
386     if (!iobbPtr) {
387       addBlock();
388 
389       iobbPtr = _miob->first_write_block();
390 
391       ink_assert(iobbPtr);
392     }
393 
394     size_t writeSize = iobbPtr->write_avail();
395 
396     if (length < writeSize) {
397       writeSize = length;
398     }
399 
400     std::memcpy(iobbPtr->end(), data, writeSize);
401     iobbPtr->fill(writeSize);
402 
403     data += writeSize;
404     length -= writeSize;
405 
406     _numWritten += writeSize;
407   }
408 
409   return *this;
410 }
411 
412 std::ostream &
413 MIOBufferWriter::operator>>(std::ostream &stream) const
414 {
415   IOBufferReader *r = _miob->alloc_reader();
416   if (r) {
417     IOBufferBlock *b;
418     while (nullptr != (b = r->get_current_block())) {
419       auto n = b->read_avail();
420       stream.write(b->start(), n);
421       r->consume(n);
422     }
423     _miob->dealloc_reader(r);
424   }
425   return stream;
426 }
427 
428 ssize_t
429 MIOBufferWriter::operator>>(int fd) const
430 {
431   ssize_t zret           = 0;
432   IOBufferReader *reader = _miob->alloc_reader();
433   if (reader) {
434     IOBufferBlock *b;
435     while (nullptr != (b = reader->get_current_block())) {
436       auto n = b->read_avail();
437       auto r = ::write(fd, b->start(), n);
438       if (r <= 0) {
439         break;
440       } else {
441         reader->consume(r);
442         zret += r;
443       }
444     }
445     _miob->dealloc_reader(reader);
446   }
447   return zret;
448 }
449