summaryrefslogtreecommitdiff
path: root/libadhocutil/buffer.h
blob: 373e79942355fb48b7a75d87a06cf541f01c95bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#ifndef ADHOCUTIL_BUFFER_H
#define ADHOCUTIL_BUFFER_H

#include "c++11Helpers.h"
#include "visibility.h"
#include <boost/format.hpp>
#include <cstdarg>
#include <string>
#include <vector>

namespace AdHoc {
	class DLL_PUBLIC Buffer;
}

namespace std {
	DLL_PUBLIC std::ostream & operator<<(std::ostream &, const AdHoc::Buffer &);
}

namespace AdHoc {

	/// High-speed text buffer for easy creation of programatically created strings.
	class DLL_PUBLIC Buffer {
	public:
		/** How should Buffer handle char * arguments? */
		enum CStringHandling {
			/// Use the memory passed verbatim.
			Use,
			/// Copy the memory passed to it for it's own use.
			Copy,
			/// Use the memory passed verbatim and free it when finished.
			Free
		};

		/// Pointer typedef.
		using Ptr = std::shared_ptr<Buffer>;
		/// Const pointer typedef.
		using CPtr = std::shared_ptr<const Buffer>;

		/** Create an empty buffer */
		Buffer() = default;
		~Buffer() = default;
		/** Create a buffer containing a single element from the given char * */
		Buffer(const char * src, CStringHandling);
		/** Create a buffer containing a single element from the given char * */
		Buffer(char * src, CStringHandling);
		/** Create a buffer containing a single element from the given std::string */
		explicit Buffer(const std::string &);

		/// Standard move/copy support
		SPECIAL_MEMBERS_DEFAULT(Buffer);

		/** Append the given char * (will copy) */
		Buffer & operator+=(const char * str);
		/** Append the given std::string */
		Buffer & operator+=(const std::string & str);
		/** Replace all current content with the given char * (will copy) */
		Buffer & operator=(const char * str);
		/** Replace all current content with the given std::string */
		Buffer & operator=(const std::string & str);
		/** true if the buffer contains no elements (empty) */
		bool operator!() const;
		/** true if the buffer contains no elements (non-empty) */
		explicit operator bool() const;
		/** Converts all elements into a single flattened std::string */
		// NOLINTNEXTLINE(hicpp-explicit-conversions)
		operator std::string() const;
		/** Converts all elements into a single flattened char * */
		// NOLINTNEXTLINE(hicpp-explicit-conversions)
		operator const char *() const;

		/**
		 * Writes all elements in turn to the given buffer space, including a null terminator.
		 * @param buf Address of buffer to write into.
		 * @param bufSize Maximum number of bytes to write.
		 * @param off Effective starting position to copy from.
		 */
		void writeto(char * buf, size_t bufSize, size_t off) const;
		/** Write the Buffer to a std::ostream */
		friend std::ostream & std::operator<<(std::ostream &, const Buffer &);

		/** Append the given char * to the end of the buffer. */
		Buffer & append(const char * str, CStringHandling h);
		/** Append the given char * to the end of the buffer. */
		Buffer & append(char * str, CStringHandling h);
		/** Append the given std::string to the end of the buffer. */
		Buffer & append(const std::string & str);
		/** Append the given printf style format string and arguments to the buffer. */
		Buffer & appendf(const char * fmt, ...) __attribute__((format(printf, 2, 3)));
		/** Append the given printf style format string and va_list to the buffer. */
		Buffer & vappendf(const char * fmt, va_list args);
		/** Append the given boost::format style format string and arguments to the buffer. */
		template<typename... Params>
		Buffer &
		appendbf(const std::string & fmtstr, const Params &... params)
		{
			auto bf = getFormat(fmtstr);
			return appendbf(bf, params...);
		}
		/** Append the given boost::format and arguments to the buffer. */
		template<typename Param, typename... Params>
		Buffer &
		appendbf(boost::format & fmt, const Param & param, const Params &... params)
		{
			fmt % param;
			return appendbf(fmt, params...);
		}
		/** Clear the Buffer of all content. */
		Buffer & clear();
		/** Get the total size of all elements. */
		size_t length() const;
		/** Test if the Buffer is empty. */
		bool empty() const;
		/** Flattern the Buffer and return it as a std::string. */
		std::string str() const;

		/** Helper function to centralize the construction of boost::format instances. */
		static boost::format getFormat(const std::string & msgfmt);

	private:
		Buffer & appendbf(boost::format & fmt);
		void DLL_PRIVATE flatten() const;

		class DLL_PRIVATE FragmentBase {
		public:
			FragmentBase() = default;
			virtual ~FragmentBase() = default;
			SPECIAL_MEMBERS_DEFAULT(FragmentBase);

			[[nodiscard]] virtual size_t length() const = 0;
			[[nodiscard]] virtual char operator[](size_t) const = 0;
			[[nodiscard]] virtual const char * c_str() const = 0;
			[[nodiscard]] virtual std::string str() const = 0;
		};

		class DLL_PRIVATE CStringFragment : public FragmentBase {
		public:
			CStringFragment(const char *, CStringHandling);
			CStringFragment(const char *, CStringHandling, size_t);
			CStringFragment(char *, CStringHandling);
			CStringFragment(char *, CStringHandling, size_t);
			SPECIAL_MEMBERS_DELETE(CStringFragment);
			~CStringFragment() override;

			[[nodiscard]] size_t length() const override;
			[[nodiscard]] char operator[](size_t) const override;
			[[nodiscard]] const char * c_str() const override;
			[[nodiscard]] std::string str() const override;

		private:
			const size_t len; // Excluding NULL term
			const char * buf;
			const CStringHandling handling;
		};

		class DLL_PRIVATE StringFragment : public FragmentBase {
		public:
			explicit StringFragment(std::string);

			[[nodiscard]] size_t length() const override;
			[[nodiscard]] char operator[](size_t) const override;
			[[nodiscard]] const char * c_str() const override;
			[[nodiscard]] std::string str() const override;

		private:
			const std::string buf;
		};

		using FragmentPtr = std::shared_ptr<FragmentBase>;
		using Content = std::vector<FragmentPtr>;
		mutable Content content;
	};

}

// libmisc compat macros
#define vstringf(...) AdHoc::Buffer().vappendf(__VA_ARGS__).str()
#define stringf(...) AdHoc::Buffer().appendf(__VA_ARGS__).str()
#define stringbf(...) AdHoc::Buffer().appendbf(__VA_ARGS__).str()

#endif