Coverage Report

Created: 2025-08-29 06:36

/src/spotify-json/include/spotify/json/codec/object.hpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015-2019 Spotify AB
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
17
#pragma once
18
19
#include <memory>
20
#include <string>
21
#include <type_traits>
22
#include <utility>
23
24
#include <spotify/json/codec/number.hpp>
25
#include <spotify/json/codec/string.hpp>
26
#include <spotify/json/decode_context.hpp>
27
#include <spotify/json/default_codec.hpp>
28
#include <spotify/json/detail/bitset.hpp>
29
#include <spotify/json/detail/field_registry.hpp>
30
#include <spotify/json/detail/macros.hpp>
31
#include <spotify/json/detail/skip_value.hpp>
32
#include <spotify/json/encode_context.hpp>
33
34
namespace spotify {
35
namespace json {
36
namespace codec {
37
namespace codec_detail {
38
39
struct object_t_base {
40
  ~object_t_base();
41
42
 protected:
43
  struct construct_untyped {
44
    virtual ~construct_untyped() = default;
45
  };
46
47
  object_t_base();
48
  object_t_base(construct_untyped *construct);
49
  object_t_base(object_t_base &&other);
50
  object_t_base(const object_t_base &other);
51
52
  void decode(decode_context &context, void *value) const;
53
  void encode(encode_context &context, const void *value) const;
54
55
  detail::field_registry _fields;
56
57
  /**
58
   * _construct may be unset, but only if T is default constructible. This is
59
   * enforced compile time by enabling the constructor that doesn't set it only
60
   * if T is default constructible.
61
   */
62
  std::shared_ptr<const construct_untyped> _construct;
63
};
64
65
}  // namespace codec_detail
66
67
template <typename T>
68
class object_t final : public codec_detail::object_t_base {
69
 public:
70
  using object_type = T;
71
72
  template <
73
      typename U = T,
74
      typename = typename std::enable_if<std::is_default_constructible<U>::value>::type>
75
2.33k
  object_t() {}
76
77
  object_t(const object_t<T> &) = default;
78
  object_t(object_t<T> &&) = default;
79
80
  template <
81
      typename create_function,
82
      typename = typename std::enable_if<!std::is_same<
83
          typename std::decay<create_function>::type,
84
          object_t>::value>::type>
85
  explicit object_t(create_function &&create)
86
      : object_t_base(new construct_function<create_function>(std::move(create))) {}
87
88
  template <typename... args_type>
89
  void optional(const std::string &name, args_type &&...args) {
90
    add_field(name, false, std::forward<args_type>(args)...);
91
  }
92
93
  template <typename... args_type>
94
2.33k
  void required(const std::string &name, args_type &&...args) {
95
2.33k
    add_field(name, true, std::forward<args_type>(args)...);
96
2.33k
  }
97
98
1.32M
  json_never_inline object_type decode(decode_context &context) const {
99
1.32M
    object_type value = construct(std::is_default_constructible<T>());
100
1.32M
    object_t_base::decode(context, &value);
101
1.32M
    return value;
102
1.32M
  }
103
104
  json_force_inline void encode(encode_context &context, const object_type &value) const {
105
    object_t_base::encode(context, &value);
106
  }
107
108
 private:
109
2.33k
  T construct(std::true_type /*is_default_constructible*/) const {
110
2.33k
    if (json_unlikely(_construct)) {
111
0
      const auto &typed = static_cast<const construct_callable &>(*_construct);
112
0
      return typed();
113
2.33k
    } else {
114
2.33k
      return object_type();
115
2.33k
    }
116
2.33k
  }
117
118
  T construct(std::false_type /*is_default_constructible*/) const {
119
    // T is not default constructible. Because _construct must be set if T is
120
    // not default constructible, there is no reason to test it in this case.
121
    const auto &typed = static_cast<const construct_callable &>(*_construct);
122
    return typed();
123
  }
124
125
  template <typename codec_type>
126
  struct codec_field : public detail::field {
127
    codec_field(bool required, size_t required_field_idx, codec_type &&codec)
128
2.33k
        : field(required, required_field_idx),
129
2.33k
          codec(std::move(codec)) {}
130
131
    codec_field(bool required, size_t required_field_idx, const codec_type &codec)
132
        : field(required, required_field_idx),
133
          codec(codec) {}
134
135
    template <typename value_type>
136
0
    void append_kv(encode_context &context, const std::string &key, const value_type &value) const {
137
0
      if (json_likely(detail::should_encode(this->codec, value))) {
138
0
        context.append(key.data(), key.size());
139
0
        this->codec.encode(context, value);
140
0
        context.append(',');
141
0
      }
142
0
    }
143
144
    codec_type codec;
145
  };
146
147
  template <typename codec_type>
148
  struct dummy_field final : public codec_field<codec_type> {
149
    dummy_field(bool required, size_t required_field_idx, codec_type &&codec)
150
        : codec_field<codec_type>(required, required_field_idx, std::move(codec)) {}
151
152
    dummy_field(bool required, size_t required_field_idx, const codec_type &codec)
153
        : codec_field<codec_type>(required, required_field_idx, codec) {}
154
155
    void decode(decode_context &context, void *) const override {
156
      this->codec.decode(context);
157
    }
158
159
    void encode(encode_context &context, const std::string &key, const void *) const override {
160
      this->append_kv(context, key, typename codec_type::object_type());
161
    }
162
  };
163
164
  template <typename member_ptr, typename codec_type>
165
  struct member_var_field final : public codec_field<codec_type> {
166
    member_var_field(bool required, size_t required_field_idx, codec_type &&codec, member_ptr member)
167
2.33k
        : codec_field<codec_type>(required, required_field_idx, std::move(codec)),
168
2.33k
          member(member) {}
169
170
    member_var_field(bool required, size_t required_field_idx, const codec_type &codec, member_ptr member)
171
        : codec_field<codec_type>(required, required_field_idx, codec),
172
          member(member) {}
173
174
1.44k
    void decode(decode_context &context, void *object) const override {
175
1.44k
      auto &typed = *static_cast<object_type *>(object);
176
1.44k
      typed.*member = this->codec.decode(context);
177
1.44k
    }
178
179
0
    void encode(encode_context &context, const std::string &key, const void *object) const override {
180
0
      const auto &typed = *static_cast<const object_type *>(object);
181
0
      const auto &value = typed.*member;
182
0
      this->append_kv(context, key, value);
183
0
    }
184
185
    member_ptr member;
186
  };
187
188
  template <typename getter_ptr, typename setter_ptr, typename codec_type>
189
  struct member_fn_field final : public codec_field<codec_type> {
190
    member_fn_field(bool required, size_t required_field_idx, codec_type &&codec, getter_ptr getter, setter_ptr setter)
191
        : codec_field<codec_type>(required, required_field_idx, std::move(codec)),
192
          getter(getter),
193
          setter(setter) {}
194
195
    member_fn_field(bool required, size_t required_field_idx, const codec_type &codec, getter_ptr getter, setter_ptr setter)
196
        : codec_field<codec_type>(required, required_field_idx, codec),
197
          getter(getter),
198
          setter(setter) {}
199
200
    void decode(decode_context &context, void *object) const override {
201
      auto &typed = *static_cast<object_type *>(object);
202
      (typed.*setter)(this->codec.decode(context));
203
    }
204
205
    void encode(encode_context &context, const std::string &key, const void *object) const override {
206
      const auto &typed = *static_cast<const object_type *>(object);
207
      const auto &value = (typed.*getter)();
208
      this->append_kv(context, key, value);
209
    }
210
211
    getter_ptr getter;
212
    setter_ptr setter;
213
  };
214
215
  template <typename getter, typename setter, typename codec_type>
216
  struct custom_field final : public codec_field<codec_type> {
217
    template <typename getter_arg, typename setter_arg>
218
    custom_field(bool required, size_t required_field_idx, codec_type &&codec, getter_arg &&get, setter_arg &&set)
219
        : codec_field<codec_type>(required, required_field_idx, std::move(codec)),
220
          get(std::forward<getter_arg>(get)),
221
          set(std::forward<setter_arg>(set)) {}
222
223
    template <typename getter_arg, typename setter_arg>
224
    custom_field(bool required, size_t required_field_idx, const codec_type &codec, getter_arg &&get, setter_arg &&set)
225
        : codec_field<codec_type>(required, required_field_idx, codec),
226
          get(std::forward<getter_arg>(get)),
227
          set(std::forward<setter_arg>(set)) {}
228
229
    void decode(decode_context &context, void *object) const override {
230
      auto &typed = *static_cast<object_type *>(object);
231
      set(typed, this->codec.decode(context));
232
    }
233
234
    void encode(encode_context &context, const std::string &key, const void *object) const override {
235
      const auto &typed = *static_cast<const object_type *>(object);
236
      const auto &value = get(typed);
237
      this->append_kv(context, key, value);
238
    }
239
240
    getter get;
241
    setter set;
242
  };
243
244
  template <typename value_type, typename object_type>
245
2.33k
  void add_field(const std::string &name, bool required, value_type object_type::*member_ptr) {
246
2.33k
    add_field(name, required, member_ptr, default_codec<value_type>());
247
2.33k
  }
248
249
  template <typename value_type, typename object_type, typename codec_type>
250
  void add_field(
251
      const std::string &name,
252
      bool required,
253
      value_type object_type::*member,
254
2.33k
      codec_type &&codec) {
255
2.33k
    using member_ptr = value_type (object_type::*);
256
2.33k
    using field_type = member_var_field<member_ptr, typename std::decay<codec_type>::type>;
257
2.33k
    _fields.save(name, required, std::shared_ptr<detail::field>(new field_type(
258
2.33k
        required,
259
2.33k
        _fields.num_required_fields(),
260
2.33k
        std::forward<codec_type>(codec),
261
2.33k
        member)));
262
2.33k
  }
263
264
  template <
265
      typename get_type,
266
      typename set_type,
267
      typename get_object_type,
268
      typename set_object_type>
269
  void add_field(
270
      const std::string &name,
271
      bool required,
272
      get_type (get_object_type::*getter)() const,
273
      void (set_object_type::*setter)(set_type)) {
274
    add_field(name, required, getter, setter, default_codec<typename std::decay<get_type>::type>());
275
  }
276
277
  template <
278
      typename get_type,
279
      typename set_type,
280
      typename get_object_type,
281
      typename set_object_type,
282
      typename codec_type>
283
  void add_field(
284
      const std::string &name,
285
      bool required,
286
      get_type (get_object_type::*getter)() const,
287
      void (set_object_type::*setter)(set_type),
288
      codec_type &&codec) {
289
    using getter_ptr = get_type (get_object_type::*)() const;
290
    using setter_ptr = void (set_object_type::*)(set_type);
291
    using field_type = member_fn_field<getter_ptr, setter_ptr, typename std::decay<codec_type>::type>;
292
    _fields.save(name, required, std::shared_ptr<detail::field>(new field_type(
293
        required,
294
        _fields.num_required_fields(),
295
        std::forward<codec_type>(codec),
296
        getter,
297
        setter)));
298
  }
299
300
  template <typename getter, typename setter>
301
  void add_field(
302
      const std::string &name,
303
      bool required,
304
      getter &&get,
305
      setter &&set) {
306
    using value_type = typename std::decay<decltype(get(std::declval<T &>()))>::type;
307
    add_field(name, required, get, set, default_codec<value_type>());
308
  }
309
310
  template <typename getter, typename setter, typename codec_type>
311
  void add_field(const std::string &name, bool required, getter &&get, setter &&set, codec_type &&codec) {
312
    using field_type = custom_field<
313
        typename std::decay<getter>::type,
314
        typename std::decay<setter>::type,
315
        typename std::decay<codec_type>::type>;
316
    _fields.save(name, required, std::shared_ptr<detail::field>(new field_type(required,
317
        _fields.num_required_fields(),
318
        std::forward<codec_type>(codec),
319
        std::forward<getter>(get),
320
        std::forward<setter>(set))));
321
  }
322
323
  template <
324
      typename codec_type,
325
      typename = typename std::enable_if<!std::is_member_pointer<codec_type>::value>::type>
326
  void add_field(const std::string &name, bool required, codec_type &&codec) {
327
    using field_type = dummy_field<typename std::decay<codec_type>::type>;
328
    _fields.save(name, required, std::shared_ptr<detail::field>(new field_type(
329
        required,
330
        _fields.num_required_fields(),
331
        std::forward<codec_type>(codec))));
332
  }
333
334
  struct construct_callable : public construct_untyped {
335
    virtual T operator()() const = 0;
336
  };
337
338
  template <typename function_type>
339
  struct construct_function final : public construct_callable {
340
    construct_function(function_type &&fn) : _fn(fn) {}
341
342
    T operator()() const override {
343
      return _fn();
344
    }
345
346
    const function_type _fn;
347
  };
348
};
349
350
template <typename T>
351
2.33k
object_t<T> object() {
352
2.33k
  return object_t<T>();
353
2.33k
}
354
355
template <typename create_function>
356
auto object(create_function &&create) -> object_t<decltype(create())> {
357
  return object_t<decltype(create())>(std::forward<create_function>(create));
358
}
359
360
}  // namespace codec
361
}  // namespace json
362
}  // namespace spotify