|  | 
|  | 1 | +#pragma once | 
|  | 2 | +/* | 
|  | 3 | + * Copyright 2010-2024 Tarantool AUTHORS: please see AUTHORS file. | 
|  | 4 | + * | 
|  | 5 | + * Redistribution and use in source and binary forms, with or | 
|  | 6 | + * without modification, are permitted provided that the following | 
|  | 7 | + * conditions are met: | 
|  | 8 | + * | 
|  | 9 | + * 1. Redistributions of source code must retain the above | 
|  | 10 | + *    copyright notice, this list of conditions and the | 
|  | 11 | + *    following disclaimer. | 
|  | 12 | + * | 
|  | 13 | + * 2. Redistributions in binary form must reproduce the above | 
|  | 14 | + *    copyright notice, this list of conditions and the following | 
|  | 15 | + *    disclaimer in the documentation and/or other materials | 
|  | 16 | + *    provided with the distribution. | 
|  | 17 | + * | 
|  | 18 | + * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND | 
|  | 19 | + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | 
|  | 20 | + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | 21 | + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | 
|  | 22 | + * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | 
|  | 23 | + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | 24 | + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | 25 | + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | 
|  | 26 | + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | 
|  | 27 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | 28 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | 
|  | 29 | + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | 30 | + * SUCH DAMAGE. | 
|  | 31 | + */ | 
|  | 32 | + | 
|  | 33 | +#include <cstdint> | 
|  | 34 | +#include <cstring> | 
|  | 35 | +#include <utility> | 
|  | 36 | + | 
|  | 37 | +#include "../Utils/CStr.hpp" | 
|  | 38 | +#include "../Utils/Traits.hpp" | 
|  | 39 | + | 
|  | 40 | +namespace mpp { | 
|  | 41 | + | 
|  | 42 | +namespace encode_details { | 
|  | 43 | + | 
|  | 44 | +/** Common data+size pair that is used for writing of variable-length data. */ | 
|  | 45 | +struct WData { | 
|  | 46 | +	const char *data; | 
|  | 47 | +	size_t size; | 
|  | 48 | +}; | 
|  | 49 | + | 
|  | 50 | +/** Random struct; used to check whether container has template write method. */ | 
|  | 51 | +struct TestWriteStruct { | 
|  | 52 | +	uint32_t a; | 
|  | 53 | +	uint16_t b; | 
|  | 54 | +}; | 
|  | 55 | + | 
|  | 56 | +/** Test that container if Buffer-like: has several needed write methods. */ | 
|  | 57 | +template <class CONT, class _ = void> | 
|  | 58 | +struct is_write_callable_h : std::false_type {}; | 
|  | 59 | + | 
|  | 60 | +template <class CONT> | 
|  | 61 | +struct is_write_callable_h<CONT, | 
|  | 62 | +	std::void_t<decltype(std::declval<CONT>().write(uint8_t{})), | 
|  | 63 | +		    decltype(std::declval<CONT>().write(uint64_t{})), | 
|  | 64 | +		    decltype(std::declval<CONT>().write(TestWriteStruct{})), | 
|  | 65 | +		    decltype(std::declval<CONT>().write({(const char *)0, 1})), | 
|  | 66 | +		    decltype(std::declval<CONT>().write(tnt::CStr<'a', 'b'>{})) | 
|  | 67 | +		   >> : std::true_type {}; | 
|  | 68 | + | 
|  | 69 | +template <class CONT> | 
|  | 70 | +constexpr bool is_write_callable_v = is_write_callable_h<CONT>::value; | 
|  | 71 | + | 
|  | 72 | +template <class CONT> | 
|  | 73 | +class BufferWriter { | 
|  | 74 | +public: | 
|  | 75 | +	explicit BufferWriter(CONT& cont_) : cont{cont_} {} | 
|  | 76 | +	void write(WData data) { cont.write({data.data, data.size}); } | 
|  | 77 | +	template <char... C> | 
|  | 78 | +	void write(tnt::CStr<C...> str) | 
|  | 79 | +	{ | 
|  | 80 | +		cont.write(std::move(str)); | 
|  | 81 | +	} | 
|  | 82 | +	template <class T> | 
|  | 83 | +	void write(T&& t) | 
|  | 84 | +	{ | 
|  | 85 | +		cont.write(std::forward<T>(t)); | 
|  | 86 | +	} | 
|  | 87 | + | 
|  | 88 | +private: | 
|  | 89 | +	CONT& cont; | 
|  | 90 | +}; | 
|  | 91 | + | 
|  | 92 | +template <class CONT> | 
|  | 93 | +class StdContWriter { | 
|  | 94 | +public: | 
|  | 95 | +	static_assert(sizeof(*std::declval<CONT>().data()) == 1); | 
|  | 96 | +	explicit StdContWriter(CONT& cont_) : cont{cont_} {} | 
|  | 97 | +	void write(WData data) | 
|  | 98 | +	{ | 
|  | 99 | +		size_t old_size = std::size(cont); | 
|  | 100 | +		cont.resize(old_size + data.size); | 
|  | 101 | +		std::memcpy(std::data(cont) + old_size, data.data, data.size); | 
|  | 102 | +	} | 
|  | 103 | +	template <char... C> | 
|  | 104 | +	void write(tnt::CStr<C...> data) | 
|  | 105 | +	{ | 
|  | 106 | +		size_t old_size = std::size(cont); | 
|  | 107 | +		cont.resize(old_size + data.size); | 
|  | 108 | +		std::memcpy(std::data(cont) + old_size, data.data, data.size); | 
|  | 109 | +	} | 
|  | 110 | + | 
|  | 111 | +	template <class T> | 
|  | 112 | +	void write(T&& t) | 
|  | 113 | +	{ | 
|  | 114 | +		static_assert(std::is_standard_layout_v<std::remove_reference_t<T>>); | 
|  | 115 | +		size_t old_size = std::size(cont); | 
|  | 116 | +		cont.resize(old_size + sizeof(T)); | 
|  | 117 | +		std::memcpy(std::data(cont) + old_size, &t, sizeof(T)); | 
|  | 118 | +	} | 
|  | 119 | + | 
|  | 120 | +private: | 
|  | 121 | +	CONT& cont; | 
|  | 122 | +}; | 
|  | 123 | + | 
|  | 124 | +template <class C> | 
|  | 125 | +class PtrWriter { | 
|  | 126 | +public: | 
|  | 127 | +	static_assert(sizeof(C) == 1); | 
|  | 128 | +	static_assert(!std::is_const_v<C>); | 
|  | 129 | +	explicit PtrWriter(C *& ptr_) : ptr{ptr_} {} | 
|  | 130 | +	void write(WData data) | 
|  | 131 | +	{ | 
|  | 132 | +		std::memcpy(ptr, data.data, data.size); | 
|  | 133 | +		ptr += data.size; | 
|  | 134 | +	} | 
|  | 135 | +	template <char... D> | 
|  | 136 | +	void write(tnt::CStr<D...> data) | 
|  | 137 | +	{ | 
|  | 138 | +		std::memcpy(ptr, data.data, data.size); | 
|  | 139 | +		ptr += data.size; | 
|  | 140 | +	} | 
|  | 141 | + | 
|  | 142 | +	template <class T> | 
|  | 143 | +	void write(T&& t) | 
|  | 144 | +	{ | 
|  | 145 | +		static_assert(std::is_standard_layout_v<std::remove_reference_t<T>>); | 
|  | 146 | +		std::memcpy(ptr, &t, sizeof(t)); | 
|  | 147 | +		ptr += sizeof(t); | 
|  | 148 | +	} | 
|  | 149 | + | 
|  | 150 | +private: | 
|  | 151 | +	C *& ptr; | 
|  | 152 | +}; | 
|  | 153 | + | 
|  | 154 | +template <class CONT> | 
|  | 155 | +auto | 
|  | 156 | +wr(CONT& cont) | 
|  | 157 | +{ | 
|  | 158 | +	if constexpr (is_write_callable_v<CONT>) | 
|  | 159 | +		return BufferWriter<CONT>{cont}; | 
|  | 160 | +	else if constexpr (tnt::is_resizable_v<CONT> && tnt::is_contiguous_v<CONT>) | 
|  | 161 | +		return StdContWriter<CONT>{cont}; | 
|  | 162 | +	else if constexpr (std::is_pointer_v<CONT>) | 
|  | 163 | +		return PtrWriter<std::remove_pointer_t<CONT>>{cont}; | 
|  | 164 | +	else | 
|  | 165 | +		static_assert(tnt::always_false_v<CONT>); | 
|  | 166 | +} | 
|  | 167 | + | 
|  | 168 | +} // namespace encode_details | 
|  | 169 | + | 
|  | 170 | +namespace decode_details { | 
|  | 171 | + | 
|  | 172 | +/** Common data+size pair that is used for reading of variable-length data. */ | 
|  | 173 | +struct RData { | 
|  | 174 | +	char *data; | 
|  | 175 | +	size_t size; | 
|  | 176 | +}; | 
|  | 177 | + | 
|  | 178 | +/** Struct that when used as read argument, means skipping of 'size' data. */ | 
|  | 179 | +struct Skip { | 
|  | 180 | +	size_t size; | 
|  | 181 | +}; | 
|  | 182 | + | 
|  | 183 | +/** Random struct; used to check whether container has template read method. */ | 
|  | 184 | +struct TestReadStruct { | 
|  | 185 | +	uint8_t a; | 
|  | 186 | +	uint64_t b; | 
|  | 187 | +}; | 
|  | 188 | + | 
|  | 189 | +/** Test that container if Buffer-like: has several needed read methods. */ | 
|  | 190 | +template <class CONT, class _ = void> | 
|  | 191 | +struct is_read_callable_h : std::false_type {}; | 
|  | 192 | + | 
|  | 193 | +template <class CONT> | 
|  | 194 | +struct is_read_callable_h<CONT, | 
|  | 195 | +	std::void_t<decltype(std::declval<CONT>().read(*(uint8_t*)0)), | 
|  | 196 | +		    decltype(std::declval<CONT>().read(*(uint64_t*)0)), | 
|  | 197 | +		    decltype(std::declval<CONT>().read(*(TestReadStruct*)0)), | 
|  | 198 | +		    decltype(std::declval<CONT>().read({(char *)0, 1})), | 
|  | 199 | +		    decltype(std::declval<CONT>().template get<uint8_t>()), | 
|  | 200 | +		    decltype(std::declval<CONT>().read({1})) | 
|  | 201 | +		   >> : std::true_type {}; | 
|  | 202 | + | 
|  | 203 | +template <class CONT> | 
|  | 204 | +constexpr bool is_read_callable_v = is_read_callable_h<CONT>::value; | 
|  | 205 | + | 
|  | 206 | +template <class CONT> | 
|  | 207 | +class BufferReader { | 
|  | 208 | +public: | 
|  | 209 | +	explicit BufferReader(CONT& cont_) : cont{cont_} {} | 
|  | 210 | +	void read(RData data) { cont.read({data.data, data.size}); } | 
|  | 211 | +	void read(Skip data) { cont.read({data.size}); } | 
|  | 212 | +	template <class T> | 
|  | 213 | +	void read(T&& t) | 
|  | 214 | +	{ | 
|  | 215 | +		cont.read(std::forward<T>(t)); | 
|  | 216 | +	} | 
|  | 217 | +	template <class T> | 
|  | 218 | +	T get() | 
|  | 219 | +	{ | 
|  | 220 | +		return cont.template get<T>(); | 
|  | 221 | +	} | 
|  | 222 | + | 
|  | 223 | +private: | 
|  | 224 | +	CONT& cont; | 
|  | 225 | +}; | 
|  | 226 | + | 
|  | 227 | +template <class C> | 
|  | 228 | +class PtrReader { | 
|  | 229 | +public: | 
|  | 230 | +	static_assert(sizeof(C) == 1); | 
|  | 231 | +	explicit PtrReader(C *& ptr_) : ptr{ptr_} {} | 
|  | 232 | +	void read(RData data) | 
|  | 233 | +	{ | 
|  | 234 | +		std::memcpy(data.data, ptr, data.size); | 
|  | 235 | +		ptr += data.size; | 
|  | 236 | +	} | 
|  | 237 | +	void read(Skip data) { ptr += data.size; } | 
|  | 238 | + | 
|  | 239 | +	template <class T> | 
|  | 240 | +	void read(T& t) | 
|  | 241 | +	{ | 
|  | 242 | +		static_assert(std::is_standard_layout_v<std::remove_reference_t<T>>); | 
|  | 243 | +		std::memcpy(&t, ptr, sizeof(t)); | 
|  | 244 | +		ptr += sizeof(t); | 
|  | 245 | +	} | 
|  | 246 | +	template <class T> | 
|  | 247 | +	T get() | 
|  | 248 | +	{ | 
|  | 249 | +		static_assert(std::is_standard_layout_v<std::remove_reference_t<T>>); | 
|  | 250 | +		T t; | 
|  | 251 | +		std::memcpy(&t, ptr, sizeof(t)); | 
|  | 252 | +		return t; | 
|  | 253 | +	} | 
|  | 254 | + | 
|  | 255 | +private: | 
|  | 256 | +	C *& ptr; | 
|  | 257 | +}; | 
|  | 258 | + | 
|  | 259 | +template <class CONT> | 
|  | 260 | +auto | 
|  | 261 | +rd(CONT& cont) | 
|  | 262 | +{ | 
|  | 263 | +	if constexpr (is_read_callable_v<CONT>) | 
|  | 264 | +		return BufferReader<CONT>{cont}; | 
|  | 265 | +	else if constexpr (std::is_pointer_v<CONT>) | 
|  | 266 | +		return PtrReader<std::remove_pointer_t<CONT>>{cont}; | 
|  | 267 | +	else | 
|  | 268 | +		static_assert(tnt::always_false_v<CONT>); | 
|  | 269 | +} | 
|  | 270 | + | 
|  | 271 | +} // namespace decode_details | 
|  | 272 | + | 
|  | 273 | +} // namespace mpp | 
0 commit comments