Skip to content

Commit 492ecfd

Browse files
committed
mpp: implement container adapter
Previously encode/decode expected the first argument (cont) to be tnt::Buffer of something with the same API. This commit introduces contaier adapter, so it is allowed now to encode to standard contiguous container (std::vector etc) and encode/decode just by pointer to data. Fix decode headers while we are here.
1 parent 4d68c52 commit 492ecfd

File tree

5 files changed

+343
-32
lines changed

5 files changed

+343
-32
lines changed

src/mpp/ContAdapter.hpp

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
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

src/mpp/Dec.hpp

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,19 @@
3333
#include <cassert>
3434
#include <functional>
3535
#include <cstdint>
36+
#include <cstring>
37+
#include <tuple>
3638
#include <utility>
39+
#include <variant>
3740

41+
#include "BSwap.hpp"
3842
#include "ClassRule.hpp"
43+
#include "ContAdapter.hpp"
3944
#include "Constants.hpp"
4045
#include "Rules.hpp"
4146
#include "Spec.hpp"
47+
#include "../Utils/CStr.hpp"
48+
#include "../Utils/Traits.hpp"
4249

4350
namespace mpp {
4451

@@ -60,7 +67,7 @@ constexpr bool is_any_putable_v =
6067
/**
6168
* If it is true, the object of type T will not be decoded - raw data will
6269
* be saved to it.
63-
*
70+
*
6471
* Now it supports only a pair of iterators (probably, wrapped with
6572
* mpp::as_raw). The check implicilty implies that BUF is an iterator, not
6673
* buffer - it would be strange to pass a pair of buffer to decoder.
@@ -411,7 +418,7 @@ auto read_value(BUF& buf)
411418
using RULE = rule_by_family_t<FAMILY>;
412419
if constexpr (SUBRULE == SIMPLEX_SUBRULE) {
413420
typename RULE::simplex_value_t tag;
414-
buf.read(tag);
421+
rd(buf).read(tag);
415422
assert(tag >= rule_simplex_tag_range_v<RULE>.first);
416423
assert(tag <= rule_simplex_tag_range_v<RULE>.last);
417424
[[maybe_unused]] typename RULE::simplex_value_t val =
@@ -427,12 +434,12 @@ auto read_value(BUF& buf)
427434
return val;
428435
} else {
429436
uint8_t tag;
430-
buf.read(tag);
437+
rd(buf).read(tag);
431438
assert(tag == RULE::complex_tag + SUBRULE);
432439
using TYPES = typename RULE::complex_types;
433440
using V = std::tuple_element_t<SUBRULE, TYPES>;
434441
under_uint_t<V> u;
435-
buf.read(u);
442+
rd(buf).read(u);
436443
V val = bswap<V>(u);
437444
return val;
438445
}
@@ -445,7 +452,7 @@ auto read_item(BUF& buf, ITEM& item)
445452
auto val = read_value<FAMILY, SUBRULE>(buf);
446453
if constexpr (RULE::has_ext) {
447454
int8_t ext_type;
448-
buf.read(ext_type);
455+
rd(buf).read(ext_type);
449456
item.ext_type = ext_type;
450457
}
451458
if constexpr (RULE::has_data) {
@@ -468,11 +475,11 @@ auto read_item(BUF& buf, ITEM& item)
468475
if (size > std::size(item))
469476
size = std::size(item);
470477
}
471-
buf.read({std::data(item), size});
478+
rd(buf).read({std::data(item), size});
472479
if constexpr (tnt::is_limited_v<ITEM> ||
473480
!tnt::is_resizable_v<ITEM>) {
474481
if (size < size_t(val))
475-
buf.read({size_t(val) - size});
482+
rd(buf).read({size_t(val) - size});
476483
}
477484
} else if constexpr (RULE::has_children) {
478485
if constexpr (tnt::is_clearable_v<ITEM>)
@@ -743,7 +750,7 @@ decode_jump(BUF& buf, T... t)
743750
{
744751
static_assert(path_item_type(PATH::last()) != PIT_BAD);
745752
static constexpr auto jumps = JumpsBuilder<PATH, BUF, T...>::build();
746-
uint8_t tag = buf.template get<uint8_t>();
753+
uint8_t tag = rd(buf).template get<uint8_t>();
747754
return jumps.data[tag](buf, t...);
748755
}
749756

@@ -922,10 +929,10 @@ bool jump_skip(BUF& buf, T... t)
922929

923930
if constexpr (RULE::has_ext) {
924931
int8_t ext_type;
925-
buf.read(ext_type);
932+
rd(buf).read(ext_type);
926933
}
927934
if constexpr (RULE::has_data) {
928-
buf.read({size_t(val)});
935+
rd(buf).read({size_t(val)});
929936
}
930937
if constexpr (RULE::has_children) {
931938
auto& arg = std::get<sizeof...(T) - 1>(std::tie(t...));
@@ -1116,7 +1123,7 @@ bool jump_find_key([[maybe_unused]] K k, tnt::iseq<>, BUF& buf, T... t)
11161123
static_assert(path_item_type(PATH::last()) == PIT_DYN_KEY);
11171124
using NEXT_PATH = path_push_t<PATH, PIT_DYN_SKIP>;
11181125
if constexpr (FAMILY == MP_STR)
1119-
buf.read({k});
1126+
rd(buf).read({k});
11201127
return decode_impl<NEXT_PATH>(buf, t..., size_t(1));
11211128
}
11221129

@@ -1143,7 +1150,7 @@ bool jump_find_key(K k, tnt::iseq<I, J...>, BUF& buf, T... t)
11431150

11441151
if (compare_key<FAMILY>(k, key, buf)) {
11451152
if constexpr (FAMILY == MP_STR)
1146-
buf.read({k});
1153+
rd(buf).read({k});
11471154
return decode_impl<NEXT_PATH>(buf, t...);
11481155
}
11491156

@@ -1274,7 +1281,7 @@ bool broken_msgpack_jump(BUF&, T...)
12741281

12751282
template <class BUF, class... T>
12761283
bool
1277-
decode(BUF& buf, T&&... t)
1284+
decode(BUF&& buf, T&&... t)
12781285
{
12791286
// TODO: Guard
12801287
bool res = decode_details::decode(buf, std::forward<T>(t)...);

0 commit comments

Comments
 (0)