1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2018 Red Hat, Inc.
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software
11   	 * Foundation.	See file COPYING.
12   	 *
13   	 */
14   	
15   	#ifndef COMMON_STACKSTRINGSTREAM_H
16   	#define COMMON_STACKSTRINGSTREAM_H
17   	
18   	#include <boost/container/small_vector.hpp>
19   	
20   	#include <algorithm>
21   	#include <iostream>
22   	#include <memory>
23   	#include <ostream>
24   	#include <sstream>
25   	#include <string_view>
26   	#include <vector>
27   	
28   	#include "include/inline_memory.h"
29   	
30   	template<std::size_t SIZE>
31   	class StackStringBuf : public std::basic_streambuf<char>
32   	{
33   	public:
34   	  StackStringBuf()
(1) Event fun_call_w_exception: Called function throws an exception of type "std::length_error". [details]
35   	    : vec{SIZE, boost::container::default_init_t{}}
36   	  {
37   	    setp(vec.data(), vec.data() + vec.size());
38   	  }
39   	  StackStringBuf(const StackStringBuf&) = delete;
40   	  StackStringBuf& operator=(const StackStringBuf&) = delete;
41   	  StackStringBuf(StackStringBuf&& o) = delete;
42   	  StackStringBuf& operator=(StackStringBuf&& o) = delete;
43   	  ~StackStringBuf() override = default;
44   	
45   	  void clear()
46   	  {
47   	    vec.resize(SIZE);
48   	    setp(vec.data(), vec.data() + SIZE);
49   	  }
50   	
51   	  std::string_view strv() const
52   	  {
53   	    return std::string_view(pbase(), pptr() - pbase());
54   	  }
55   	
56   	protected:
57   	  std::streamsize xsputn(const char *s, std::streamsize n)
58   	  {
59   	    std::streamsize capacity = epptr() - pptr();
60   	    std::streamsize left = n;
61   	    if (capacity >= left) {
62   	      maybe_inline_memcpy(pptr(), s, left, 32);
63   	      pbump(left);
64   	    } else {
65   	      maybe_inline_memcpy(pptr(), s, capacity, 64);
66   	      s += capacity;
67   	      left -= capacity;
68   	      vec.insert(vec.end(), s, s + left);
69   	      setp(vec.data(), vec.data() + vec.size());
70   	      pbump(vec.size());
71   	    }
72   	    return n;
73   	  }
74   	
75   	  int overflow(int c)
76   	  {
77   	    if (traits_type::not_eof(c)) {
78   	      char str = traits_type::to_char_type(c);
79   	      vec.push_back(str);
80   	      return c;
81   	    } else {
82   	      return traits_type::eof();
83   	    }
84   	  }
85   	
86   	private:
87   	
88   	  boost::container::small_vector<char, SIZE> vec;
89   	};
90   	
91   	template<std::size_t SIZE>
92   	class StackStringStream : public std::basic_ostream<char>
93   	{
94   	public:
(1) Event fun_call_w_exception: Called function throws an exception of type "std::length_error". [details]
95   	  StackStringStream() : basic_ostream<char>(&ssb), default_fmtflags(flags()) {}
96   	  StackStringStream(const StackStringStream& o) = delete;
97   	  StackStringStream& operator=(const StackStringStream& o) = delete;
98   	  StackStringStream(StackStringStream&& o) = delete;
99   	  StackStringStream& operator=(StackStringStream&& o) = delete;
100  	  ~StackStringStream() override = default;
101  	
102  	  void reset() {
103  	    clear(); /* reset state flags */
104  	    flags(default_fmtflags); /* reset fmtflags to constructor defaults */
105  	    ssb.clear();
106  	  }
107  	
108  	  std::string_view strv() const {
109  	    return ssb.strv();
110  	  }
111  	
112  	private:
113  	  StackStringBuf<SIZE> ssb;
114  	  fmtflags const default_fmtflags;
115  	};
116  	
117  	/* In an ideal world, we could use StackStringStream indiscriminately, but alas
118  	 * it's very expensive to construct/destruct. So, we cache them in a
119  	 * thread_local vector. DO NOT share these with other threads. The copy/move
120  	 * constructors are deliberately restrictive to make this more difficult to
121  	 * accidentally do.
122  	 */
123  	class CachedStackStringStream {
124  	public:
125  	  using sss = StackStringStream<4096>;
126  	  using osptr = std::unique_ptr<sss>;
127  	
128  	  CachedStackStringStream() {
129  	    if (cache.destructed || cache.c.empty()) {
(1) Event fun_call_w_exception: Called function throws an exception of type "std::length_error". [details]
130  	      osp = std::make_unique<sss>();
131  	    } else {
132  	      osp = std::move(cache.c.back());
133  	      cache.c.pop_back();
134  	      osp->reset();
135  	    }
136  	  }
137  	  CachedStackStringStream(const CachedStackStringStream&) = delete;
138  	  CachedStackStringStream& operator=(const CachedStackStringStream&) = delete;
139  	  CachedStackStringStream(CachedStackStringStream&&) = delete;
140  	  CachedStackStringStream& operator=(CachedStackStringStream&&) = delete;
141  	  ~CachedStackStringStream() {
142  	    if (!cache.destructed && cache.c.size() < max_elems) {
143  	      cache.c.emplace_back(std::move(osp));
144  	    }
145  	  }
146  	
147  	  sss& operator*() {
148  	    return *osp;
149  	  }
150  	  sss const& operator*() const {
151  	    return *osp;
152  	  }
153  	  sss* operator->() {
154  	    return osp.get();
155  	  }
156  	  sss const* operator->() const {
157  	    return osp.get();
158  	  }
159  	
160  	  sss const* get() const {
161  	    return osp.get();
162  	  }
163  	  sss* get() {
164  	    return osp.get();
165  	  }
166  	
167  	private:
168  	  static constexpr std::size_t max_elems = 8;
169  	
170  	  /* The thread_local cache may be destructed before other static structures.
171  	   * If those destructors try to create a CachedStackStringStream (e.g. for
172  	   * logging) and access this cache, that access will be undefined. So note if
173  	   * the cache has been destructed and check before use.
174  	   */
175  	  struct Cache {
176  	    using container = std::vector<osptr>;
177  	
178  	    Cache() {}
179  	    ~Cache() { destructed = true; }
180  	
181  	    container c;
182  	    bool destructed = false;
183  	  };
184  	
185  	  inline static thread_local Cache cache;
186  	  osptr osp;
187  	};
188  	
189  	#endif
190