aboutsummaryrefslogtreecommitdiff
path: root/mojo/public/cpp/bindings/lib/native_struct_serialization.h
blob: 457435b9558d8dda5a76d57e38566f7d879684b9 (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
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_H_

#include <stddef.h>
#include <stdint.h>

#include <limits>

#include "base/logging.h"
#include "base/pickle.h"
#include "ipc/ipc_param_traits.h"
#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
#include "mojo/public/cpp/bindings/native_struct.h"
#include "mojo/public/cpp/bindings/native_struct_data_view.h"

namespace mojo {
namespace internal {

template <typename MaybeConstUserType>
struct NativeStructSerializerImpl {
  using UserType = typename std::remove_const<MaybeConstUserType>::type;
  using Traits = IPC::ParamTraits<UserType>;

  static size_t PrepareToSerialize(MaybeConstUserType& value,
                                   SerializationContext* context) {
    base::PickleSizer sizer;
    Traits::GetSize(&sizer, value);
    return Align(sizer.payload_size() + sizeof(ArrayHeader));
  }

  static void Serialize(MaybeConstUserType& value,
                        Buffer* buffer,
                        NativeStruct_Data** out,
                        SerializationContext* context) {
    base::Pickle pickle;
    Traits::Write(&pickle, value);

#if DCHECK_IS_ON()
    base::PickleSizer sizer;
    Traits::GetSize(&sizer, value);
    DCHECK_EQ(sizer.payload_size(), pickle.payload_size());
#endif

    size_t total_size = pickle.payload_size() + sizeof(ArrayHeader);
    DCHECK_LT(total_size, std::numeric_limits<uint32_t>::max());

    // Allocate a uint8 array, initialize its header, and copy the Pickle in.
    ArrayHeader* header =
        reinterpret_cast<ArrayHeader*>(buffer->Allocate(total_size));
    header->num_bytes = static_cast<uint32_t>(total_size);
    header->num_elements = static_cast<uint32_t>(pickle.payload_size());
    memcpy(reinterpret_cast<char*>(header) + sizeof(ArrayHeader),
           pickle.payload(), pickle.payload_size());

    *out = reinterpret_cast<NativeStruct_Data*>(header);
  }

  static bool Deserialize(NativeStruct_Data* data,
                          UserType* out,
                          SerializationContext* context) {
    if (!data)
      return false;

    // Construct a temporary base::Pickle view over the array data. Note that
    // the Array_Data is laid out like this:
    //
    //   [num_bytes (4 bytes)] [num_elements (4 bytes)] [elements...]
    //
    // and base::Pickle expects to view data like this:
    //
    //   [payload_size (4 bytes)] [header bytes ...] [payload...]
    //
    // Because ArrayHeader's num_bytes includes the length of the header and
    // Pickle's payload_size does not, we need to adjust the stored value
    // momentarily so Pickle can view the data.
    ArrayHeader* header = reinterpret_cast<ArrayHeader*>(data);
    DCHECK_GE(header->num_bytes, sizeof(ArrayHeader));
    header->num_bytes -= sizeof(ArrayHeader);

    {
      // Construct a view over the full Array_Data, including our hacked up
      // header. Pickle will infer from this that the header is 8 bytes long,
      // and the payload will contain all of the pickled bytes.
      base::Pickle pickle_view(reinterpret_cast<const char*>(header),
                               header->num_bytes + sizeof(ArrayHeader));
      base::PickleIterator iter(pickle_view);
      if (!Traits::Read(&pickle_view, &iter, out))
        return false;
    }

    // Return the header to its original state.
    header->num_bytes += sizeof(ArrayHeader);

    return true;
  }
};

struct MOJO_CPP_BINDINGS_EXPORT UnmappedNativeStructSerializerImpl {
  static size_t PrepareToSerialize(const NativeStructPtr& input,
                                   SerializationContext* context);
  static void Serialize(const NativeStructPtr& input,
                        Buffer* buffer,
                        NativeStruct_Data** output,
                        SerializationContext* context);
  static bool Deserialize(NativeStruct_Data* input,
                          NativeStructPtr* output,
                          SerializationContext* context);
};

template <>
struct NativeStructSerializerImpl<NativeStructPtr>
    : public UnmappedNativeStructSerializerImpl {};

template <>
struct NativeStructSerializerImpl<const NativeStructPtr>
    : public UnmappedNativeStructSerializerImpl {};

template <typename MaybeConstUserType>
struct Serializer<NativeStructDataView, MaybeConstUserType>
    : public NativeStructSerializerImpl<MaybeConstUserType> {};

}  // namespace internal
}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_NATIVE_STRUCT_SERIALIZATION_H_