]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/StackStringStream.h
import quincy beta 17.1.0
[ceph.git] / ceph / src / common / StackStringStream.h
CommitLineData
11fdf7f2
TL
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
30template<std::size_t SIZE>
31class StackStringBuf : public std::basic_streambuf<char>
32{
33public:
34 StackStringBuf()
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
56protected:
20effc67 57 std::streamsize xsputn(const char *s, std::streamsize n) final
11fdf7f2
TL
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
20effc67 75 int overflow(int c) final
11fdf7f2
TL
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
86private:
87
88 boost::container::small_vector<char, SIZE> vec;
89};
90
91template<std::size_t SIZE>
92class StackStringStream : public std::basic_ostream<char>
93{
94public:
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 }
f67539c2
TL
111 std::string str() const {
112 return std::string(ssb.strv());
113 }
11fdf7f2
TL
114
115private:
116 StackStringBuf<SIZE> ssb;
117 fmtflags const default_fmtflags;
118};
119
120/* In an ideal world, we could use StackStringStream indiscriminately, but alas
121 * it's very expensive to construct/destruct. So, we cache them in a
122 * thread_local vector. DO NOT share these with other threads. The copy/move
123 * constructors are deliberately restrictive to make this more difficult to
124 * accidentally do.
125 */
126class CachedStackStringStream {
127public:
128 using sss = StackStringStream<4096>;
129 using osptr = std::unique_ptr<sss>;
130
131 CachedStackStringStream() {
132 if (cache.destructed || cache.c.empty()) {
133 osp = std::make_unique<sss>();
134 } else {
135 osp = std::move(cache.c.back());
136 cache.c.pop_back();
137 osp->reset();
138 }
139 }
140 CachedStackStringStream(const CachedStackStringStream&) = delete;
141 CachedStackStringStream& operator=(const CachedStackStringStream&) = delete;
142 CachedStackStringStream(CachedStackStringStream&&) = delete;
143 CachedStackStringStream& operator=(CachedStackStringStream&&) = delete;
144 ~CachedStackStringStream() {
145 if (!cache.destructed && cache.c.size() < max_elems) {
146 cache.c.emplace_back(std::move(osp));
147 }
148 }
149
150 sss& operator*() {
151 return *osp;
152 }
153 sss const& operator*() const {
154 return *osp;
155 }
156 sss* operator->() {
157 return osp.get();
158 }
159 sss const* operator->() const {
160 return osp.get();
161 }
162
163 sss const* get() const {
164 return osp.get();
165 }
166 sss* get() {
167 return osp.get();
168 }
169
170private:
171 static constexpr std::size_t max_elems = 8;
172
173 /* The thread_local cache may be destructed before other static structures.
174 * If those destructors try to create a CachedStackStringStream (e.g. for
175 * logging) and access this cache, that access will be undefined. So note if
176 * the cache has been destructed and check before use.
177 */
178 struct Cache {
179 using container = std::vector<osptr>;
180
181 Cache() {}
182 ~Cache() { destructed = true; }
183
184 container c;
185 bool destructed = false;
186 };
187
188 inline static thread_local Cache cache;
189 osptr osp;
190};
191
192#endif