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