1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ppapi/proxy/raw_var_data.h"
9 #include "base/containers/hash_tables.h"
10 #include "base/stl_util.h"
11 #include "ipc/ipc_message.h"
12 #include "ppapi/proxy/ppapi_param_traits.h"
13 #include "ppapi/shared_impl/array_var.h"
14 #include "ppapi/shared_impl/dictionary_var.h"
15 #include "ppapi/shared_impl/ppapi_globals.h"
16 #include "ppapi/shared_impl/resource_var.h"
17 #include "ppapi/shared_impl/scoped_pp_var.h"
18 #include "ppapi/shared_impl/var.h"
19 #include "ppapi/shared_impl/var_tracker.h"
28 // When sending array buffers, if the size is over 256K, we use shared
29 // memory instead of sending the data over IPC. Light testing suggests
30 // shared memory is much faster for 256K and larger messages.
31 static const uint32 kMinimumArrayBufferSizeForShmem
= 256 * 1024;
32 static uint32 g_minimum_array_buffer_size_for_shmem
=
33 kMinimumArrayBufferSizeForShmem
;
36 StackEntry(PP_Var v
, size_t i
) : var(v
), data_index(i
) {}
41 // For a given PP_Var, returns the RawVarData associated with it, or creates a
42 // new one if there is no existing one. The data is appended to |data| if it
43 // is newly created. The index into |data| pointing to the result is returned.
44 // |visited_map| keeps track of RawVarDatas that have already been created.
45 size_t GetOrCreateRawVarData(const PP_Var
& var
,
46 base::hash_map
<int64_t, size_t>* visited_map
,
47 ScopedVector
<RawVarData
>* data
) {
48 if (VarTracker::IsVarTypeRefcounted(var
.type
)) {
49 base::hash_map
<int64_t, size_t>::iterator it
= visited_map
->find(
51 if (it
!= visited_map
->end()) {
54 data
->push_back(RawVarData::Create(var
.type
));
55 (*visited_map
)[var
.value
.as_id
] = data
->size() - 1;
58 data
->push_back(RawVarData::Create(var
.type
));
60 return data
->size() - 1;
63 bool CanHaveChildren(PP_Var var
) {
64 return var
.type
== PP_VARTYPE_ARRAY
|| var
.type
== PP_VARTYPE_DICTIONARY
;
69 // RawVarDataGraph ------------------------------------------------------------
70 RawVarDataGraph::RawVarDataGraph() {
73 RawVarDataGraph::~RawVarDataGraph() {
76 // This function uses a stack-based DFS search to traverse the var graph. Each
77 // iteration, the top node on the stack examined. If the node has not been
78 // visited yet (i.e. !initialized()) then it is added to the list of
79 // |parent_ids| which contains all of the nodes on the path from the start node
80 // to the current node. Each of that nodes children are examined. If they appear
81 // in the list of |parent_ids| it means we have a cycle and we return NULL.
82 // Otherwise, if they haven't been visited yet we add them to the stack, If the
83 // node at the top of the stack has already been visited, then we pop it off the
84 // stack and erase it from |parent_ids|.
86 scoped_ptr
<RawVarDataGraph
> RawVarDataGraph::Create(const PP_Var
& var
,
87 PP_Instance instance
) {
88 scoped_ptr
<RawVarDataGraph
> graph(new RawVarDataGraph
);
89 // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph.
90 base::hash_map
<int64_t, size_t> visited_map
;
91 base::hash_set
<int64_t> parent_ids
;
93 std::stack
<StackEntry
> stack
;
94 stack
.push(StackEntry(var
, GetOrCreateRawVarData(var
, &visited_map
,
97 while (!stack
.empty()) {
98 PP_Var current_var
= stack
.top().var
;
99 RawVarData
* current_var_data
= graph
->data_
[stack
.top().data_index
];
101 if (current_var_data
->initialized()) {
103 if (CanHaveChildren(current_var
))
104 parent_ids
.erase(current_var
.value
.as_id
);
108 if (CanHaveChildren(current_var
))
109 parent_ids
.insert(current_var
.value
.as_id
);
110 if (!current_var_data
->Init(current_var
, instance
)) {
112 return scoped_ptr
<RawVarDataGraph
>();
115 // Add child nodes to the stack.
116 if (current_var
.type
== PP_VARTYPE_ARRAY
) {
117 ArrayVar
* array_var
= ArrayVar::FromPPVar(current_var
);
120 return scoped_ptr
<RawVarDataGraph
>();
122 for (ArrayVar::ElementVector::const_iterator iter
=
123 array_var
->elements().begin();
124 iter
!= array_var
->elements().end();
126 const PP_Var
& child
= iter
->get();
127 // If a child of this node is already in parent_ids, we have a cycle so
128 // we just return null.
129 if (CanHaveChildren(child
) && parent_ids
.count(child
.value
.as_id
) != 0)
130 return scoped_ptr
<RawVarDataGraph
>();
131 size_t child_id
= GetOrCreateRawVarData(child
, &visited_map
,
133 static_cast<ArrayRawVarData
*>(current_var_data
)->AddChild(child_id
);
134 if (!graph
->data_
[child_id
]->initialized())
135 stack
.push(StackEntry(child
, child_id
));
137 } else if (current_var
.type
== PP_VARTYPE_DICTIONARY
) {
138 DictionaryVar
* dict_var
= DictionaryVar::FromPPVar(current_var
);
141 return scoped_ptr
<RawVarDataGraph
>();
143 for (DictionaryVar::KeyValueMap::const_iterator iter
=
144 dict_var
->key_value_map().begin();
145 iter
!= dict_var
->key_value_map().end();
147 const PP_Var
& child
= iter
->second
.get();
148 if (CanHaveChildren(child
) && parent_ids
.count(child
.value
.as_id
) != 0)
149 return scoped_ptr
<RawVarDataGraph
>();
150 size_t child_id
= GetOrCreateRawVarData(child
, &visited_map
,
152 static_cast<DictionaryRawVarData
*>(
153 current_var_data
)->AddChild(iter
->first
, child_id
);
154 if (!graph
->data_
[child_id
]->initialized())
155 stack
.push(StackEntry(child
, child_id
));
162 PP_Var
RawVarDataGraph::CreatePPVar(PP_Instance instance
) {
163 // Create and initialize each node in the graph.
164 std::vector
<PP_Var
> graph
;
165 for (size_t i
= 0; i
< data_
.size(); ++i
)
166 graph
.push_back(data_
[i
]->CreatePPVar(instance
));
167 for (size_t i
= 0; i
< data_
.size(); ++i
)
168 data_
[i
]->PopulatePPVar(graph
[i
], graph
);
169 // Everything except the root will have one extra ref. Remove that ref.
170 for (size_t i
= 1; i
< data_
.size(); ++i
)
171 ScopedPPVar(ScopedPPVar::PassRef(), graph
[i
]);
172 // The first element is the root.
176 void RawVarDataGraph::Write(IPC::Message
* m
,
177 const HandleWriter
& handle_writer
) {
178 // Write the size, followed by each node in the graph.
179 m
->WriteUInt32(static_cast<uint32_t>(data_
.size()));
180 for (size_t i
= 0; i
< data_
.size(); ++i
) {
181 m
->WriteInt(data_
[i
]->Type());
182 data_
[i
]->Write(m
, handle_writer
);
187 scoped_ptr
<RawVarDataGraph
> RawVarDataGraph::Read(const IPC::Message
* m
,
188 base::PickleIterator
* iter
) {
189 scoped_ptr
<RawVarDataGraph
> result(new RawVarDataGraph
);
191 if (!iter
->ReadUInt32(&size
))
192 return scoped_ptr
<RawVarDataGraph
>();
193 for (uint32_t i
= 0; i
< size
; ++i
) {
195 if (!iter
->ReadInt(&type
))
196 return scoped_ptr
<RawVarDataGraph
>();
197 PP_VarType var_type
= static_cast<PP_VarType
>(type
);
198 result
->data_
.push_back(RawVarData::Create(var_type
));
199 if (!result
->data_
.back()->Read(var_type
, m
, iter
))
200 return scoped_ptr
<RawVarDataGraph
>();
202 return result
.Pass();
205 std::vector
<SerializedHandle
*> RawVarDataGraph::GetHandles() {
206 std::vector
<SerializedHandle
*> result
;
207 for (size_t i
= 0; i
< data_
.size(); ++i
) {
208 SerializedHandle
* handle
= data_
[i
]->GetHandle();
210 result
.push_back(handle
);
216 void RawVarDataGraph::SetMinimumArrayBufferSizeForShmemForTest(
219 g_minimum_array_buffer_size_for_shmem
= kMinimumArrayBufferSizeForShmem
;
221 g_minimum_array_buffer_size_for_shmem
= threshold
;
224 // RawVarData ------------------------------------------------------------------
227 RawVarData
* RawVarData::Create(PP_VarType type
) {
229 case PP_VARTYPE_UNDEFINED
:
230 case PP_VARTYPE_NULL
:
231 case PP_VARTYPE_BOOL
:
232 case PP_VARTYPE_INT32
:
233 case PP_VARTYPE_DOUBLE
:
234 case PP_VARTYPE_OBJECT
:
235 return new BasicRawVarData();
236 case PP_VARTYPE_STRING
:
237 return new StringRawVarData();
238 case PP_VARTYPE_ARRAY_BUFFER
:
239 return new ArrayBufferRawVarData();
240 case PP_VARTYPE_ARRAY
:
241 return new ArrayRawVarData();
242 case PP_VARTYPE_DICTIONARY
:
243 return new DictionaryRawVarData();
244 case PP_VARTYPE_RESOURCE
:
245 return new ResourceRawVarData();
251 RawVarData::RawVarData() : initialized_(false) {
254 RawVarData::~RawVarData() {
257 SerializedHandle
* RawVarData::GetHandle() {
261 // BasicRawVarData -------------------------------------------------------------
262 BasicRawVarData::BasicRawVarData() {
265 BasicRawVarData::~BasicRawVarData() {
268 PP_VarType
BasicRawVarData::Type() {
272 bool BasicRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
278 PP_Var
BasicRawVarData::CreatePPVar(PP_Instance instance
) {
282 void BasicRawVarData::PopulatePPVar(const PP_Var
& var
,
283 const std::vector
<PP_Var
>& graph
) {
286 void BasicRawVarData::Write(
288 const HandleWriter
& handle_writer
) {
290 case PP_VARTYPE_UNDEFINED
:
291 case PP_VARTYPE_NULL
:
292 // These don't need any data associated with them other than the type we
295 case PP_VARTYPE_BOOL
:
296 m
->WriteBool(PP_ToBool(var_
.value
.as_bool
));
298 case PP_VARTYPE_INT32
:
299 m
->WriteInt(var_
.value
.as_int
);
301 case PP_VARTYPE_DOUBLE
:
302 IPC::ParamTraits
<double>::Write(m
, var_
.value
.as_double
);
304 case PP_VARTYPE_OBJECT
:
305 m
->WriteInt64(var_
.value
.as_id
);
313 bool BasicRawVarData::Read(PP_VarType type
,
314 const IPC::Message
* m
,
315 base::PickleIterator
* iter
) {
319 case PP_VARTYPE_UNDEFINED
:
320 case PP_VARTYPE_NULL
:
321 // These don't have any data associated with them other than the type we
322 // just deserialized.
324 case PP_VARTYPE_BOOL
: {
326 if (!iter
->ReadBool(&bool_value
))
328 result
.value
.as_bool
= PP_FromBool(bool_value
);
331 case PP_VARTYPE_INT32
:
332 if (!iter
->ReadInt(&result
.value
.as_int
))
335 case PP_VARTYPE_DOUBLE
:
336 if (!IPC::ParamTraits
<double>::Read(m
, iter
, &result
.value
.as_double
))
339 case PP_VARTYPE_OBJECT
:
340 if (!iter
->ReadInt64(&result
.value
.as_id
))
351 // StringRawVarData ------------------------------------------------------------
352 StringRawVarData::StringRawVarData() {
355 StringRawVarData::~StringRawVarData() {
358 PP_VarType
StringRawVarData::Type() {
359 return PP_VARTYPE_STRING
;
362 bool StringRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
363 DCHECK(var
.type
== PP_VARTYPE_STRING
);
364 StringVar
* string_var
= StringVar::FromPPVar(var
);
367 data_
= string_var
->value();
372 PP_Var
StringRawVarData::CreatePPVar(PP_Instance instance
) {
373 return StringVar::SwapValidatedUTF8StringIntoPPVar(&data_
);
376 void StringRawVarData::PopulatePPVar(const PP_Var
& var
,
377 const std::vector
<PP_Var
>& graph
) {
380 void StringRawVarData::Write(IPC::Message
* m
,
381 const HandleWriter
& handle_writer
) {
382 m
->WriteString(data_
);
385 bool StringRawVarData::Read(PP_VarType type
,
386 const IPC::Message
* m
,
387 base::PickleIterator
* iter
) {
388 if (!iter
->ReadString(&data_
))
393 // ArrayBufferRawVarData -------------------------------------------------------
394 ArrayBufferRawVarData::ArrayBufferRawVarData() {
397 ArrayBufferRawVarData::~ArrayBufferRawVarData() {
400 PP_VarType
ArrayBufferRawVarData::Type() {
401 return PP_VARTYPE_ARRAY_BUFFER
;
404 bool ArrayBufferRawVarData::Init(const PP_Var
& var
,
405 PP_Instance instance
) {
406 DCHECK(var
.type
== PP_VARTYPE_ARRAY_BUFFER
);
407 ArrayBufferVar
* buffer_var
= ArrayBufferVar::FromPPVar(var
);
410 bool using_shmem
= false;
411 if (buffer_var
->ByteLength() >= g_minimum_array_buffer_size_for_shmem
&&
414 base::SharedMemoryHandle plugin_handle
;
415 using_shmem
= buffer_var
->CopyToNewShmem(instance
,
419 if (host_handle_id
!= -1) {
420 DCHECK(!base::SharedMemory::IsHandleValid(plugin_handle
));
421 DCHECK(PpapiGlobals::Get()->IsPluginGlobals());
422 type_
= ARRAY_BUFFER_SHMEM_HOST
;
423 host_shm_handle_id_
= host_handle_id
;
425 DCHECK(base::SharedMemory::IsHandleValid(plugin_handle
));
426 DCHECK(PpapiGlobals::Get()->IsHostGlobals());
427 type_
= ARRAY_BUFFER_SHMEM_PLUGIN
;
428 plugin_shm_handle_
= SerializedHandle(plugin_handle
,
429 buffer_var
->ByteLength());
434 type_
= ARRAY_BUFFER_NO_SHMEM
;
435 data_
= std::string(static_cast<const char*>(buffer_var
->Map()),
436 buffer_var
->ByteLength());
442 PP_Var
ArrayBufferRawVarData::CreatePPVar(PP_Instance instance
) {
443 PP_Var result
= PP_MakeUndefined();
445 case ARRAY_BUFFER_SHMEM_HOST
: {
446 base::SharedMemoryHandle host_handle
;
447 uint32 size_in_bytes
;
448 bool ok
= PpapiGlobals::Get()->GetVarTracker()->
449 StopTrackingSharedMemoryHandle(host_shm_handle_id_
,
454 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
455 size_in_bytes
, host_handle
);
457 LOG(ERROR
) << "Couldn't find array buffer id: " << host_shm_handle_id_
;
458 return PP_MakeUndefined();
462 case ARRAY_BUFFER_SHMEM_PLUGIN
: {
463 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
464 plugin_shm_handle_
.size(),
465 plugin_shm_handle_
.shmem());
468 case ARRAY_BUFFER_NO_SHMEM
: {
469 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
470 static_cast<uint32
>(data_
.size()), data_
.data());
475 return PP_MakeUndefined();
477 DCHECK(result
.type
== PP_VARTYPE_ARRAY_BUFFER
);
481 void ArrayBufferRawVarData::PopulatePPVar(const PP_Var
& var
,
482 const std::vector
<PP_Var
>& graph
) {
485 void ArrayBufferRawVarData::Write(
487 const HandleWriter
& handle_writer
) {
490 case ARRAY_BUFFER_SHMEM_HOST
:
491 m
->WriteInt(host_shm_handle_id_
);
493 case ARRAY_BUFFER_SHMEM_PLUGIN
:
494 handle_writer
.Run(m
, plugin_shm_handle_
);
496 case ARRAY_BUFFER_NO_SHMEM
:
497 m
->WriteString(data_
);
502 bool ArrayBufferRawVarData::Read(PP_VarType type
,
503 const IPC::Message
* m
,
504 base::PickleIterator
* iter
) {
506 if (!iter
->ReadInt(&shmem_type
))
508 type_
= static_cast<ShmemType
>(shmem_type
);
510 case ARRAY_BUFFER_SHMEM_HOST
:
511 if (!iter
->ReadInt(&host_shm_handle_id_
))
514 case ARRAY_BUFFER_SHMEM_PLUGIN
:
515 if (!IPC::ParamTraits
<SerializedHandle
>::Read(
516 m
, iter
, &plugin_shm_handle_
)) {
520 case ARRAY_BUFFER_NO_SHMEM
:
521 if (!iter
->ReadString(&data_
))
525 // We read an invalid ID.
532 SerializedHandle
* ArrayBufferRawVarData::GetHandle() {
533 if (type_
== ARRAY_BUFFER_SHMEM_PLUGIN
&& plugin_shm_handle_
.size() != 0)
534 return &plugin_shm_handle_
;
538 // ArrayRawVarData -------------------------------------------------------------
539 ArrayRawVarData::ArrayRawVarData() {
542 ArrayRawVarData::~ArrayRawVarData() {
545 void ArrayRawVarData::AddChild(size_t element
) {
546 children_
.push_back(element
);
549 PP_VarType
ArrayRawVarData::Type() {
550 return PP_VARTYPE_ARRAY
;
553 bool ArrayRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
555 DCHECK(var
.type
== PP_VARTYPE_ARRAY
);
560 PP_Var
ArrayRawVarData::CreatePPVar(PP_Instance instance
) {
561 return (new ArrayVar())->GetPPVar();
564 void ArrayRawVarData::PopulatePPVar(const PP_Var
& var
,
565 const std::vector
<PP_Var
>& graph
) {
566 if (var
.type
!= PP_VARTYPE_ARRAY
) {
570 ArrayVar
* array_var
= ArrayVar::FromPPVar(var
);
571 DCHECK(array_var
->elements().empty());
572 for (size_t i
= 0; i
< children_
.size(); ++i
)
573 array_var
->elements().push_back(ScopedPPVar(graph
[children_
[i
]]));
576 void ArrayRawVarData::Write(IPC::Message
* m
,
577 const HandleWriter
& handle_writer
) {
578 m
->WriteUInt32(static_cast<uint32_t>(children_
.size()));
579 for (size_t i
= 0; i
< children_
.size(); ++i
)
580 m
->WriteUInt32(static_cast<uint32_t>(children_
[i
]));
583 bool ArrayRawVarData::Read(PP_VarType type
,
584 const IPC::Message
* m
,
585 base::PickleIterator
* iter
) {
587 if (!iter
->ReadUInt32(&size
))
589 for (uint32_t i
= 0; i
< size
; ++i
) {
591 if (!iter
->ReadUInt32(&index
))
593 children_
.push_back(index
);
598 // DictionaryRawVarData --------------------------------------------------------
599 DictionaryRawVarData::DictionaryRawVarData() {
602 DictionaryRawVarData::~DictionaryRawVarData() {
605 void DictionaryRawVarData::AddChild(const std::string
& key
,
607 children_
.push_back(make_pair(key
, value
));
610 PP_VarType
DictionaryRawVarData::Type() {
611 return PP_VARTYPE_DICTIONARY
;
614 bool DictionaryRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
615 DCHECK(var
.type
== PP_VARTYPE_DICTIONARY
);
620 PP_Var
DictionaryRawVarData::CreatePPVar(PP_Instance instance
) {
621 return (new DictionaryVar())->GetPPVar();
624 void DictionaryRawVarData::PopulatePPVar(const PP_Var
& var
,
625 const std::vector
<PP_Var
>& graph
) {
626 if (var
.type
!= PP_VARTYPE_DICTIONARY
) {
630 DictionaryVar
* dictionary_var
= DictionaryVar::FromPPVar(var
);
631 DCHECK(dictionary_var
->key_value_map().empty());
632 for (size_t i
= 0; i
< children_
.size(); ++i
) {
633 bool success
= dictionary_var
->SetWithStringKey(children_
[i
].first
,
634 graph
[children_
[i
].second
]);
639 void DictionaryRawVarData::Write(
641 const HandleWriter
& handle_writer
) {
642 m
->WriteUInt32(static_cast<uint32_t>(children_
.size()));
643 for (size_t i
= 0; i
< children_
.size(); ++i
) {
644 m
->WriteString(children_
[i
].first
);
645 m
->WriteUInt32(static_cast<uint32_t>(children_
[i
].second
));
649 bool DictionaryRawVarData::Read(PP_VarType type
,
650 const IPC::Message
* m
,
651 base::PickleIterator
* iter
) {
653 if (!iter
->ReadUInt32(&size
))
655 for (uint32_t i
= 0; i
< size
; ++i
) {
658 if (!iter
->ReadString(&key
))
660 if (!iter
->ReadUInt32(&value
))
662 children_
.push_back(make_pair(key
, value
));
667 // ResourceRawVarData ----------------------------------------------------------
668 ResourceRawVarData::ResourceRawVarData()
670 pending_renderer_host_id_(0),
671 pending_browser_host_id_(0) {}
673 ResourceRawVarData::~ResourceRawVarData() {
676 PP_VarType
ResourceRawVarData::Type() {
677 return PP_VARTYPE_RESOURCE
;
680 bool ResourceRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
681 DCHECK(var
.type
== PP_VARTYPE_RESOURCE
);
682 ResourceVar
* resource_var
= ResourceVar::FromPPVar(var
);
685 pp_resource_
= resource_var
->GetPPResource();
686 const IPC::Message
* message
= resource_var
->GetCreationMessage();
688 creation_message_
.reset(new IPC::Message(*message
));
690 creation_message_
.reset();
691 pending_renderer_host_id_
= resource_var
->GetPendingRendererHostId();
692 pending_browser_host_id_
= resource_var
->GetPendingBrowserHostId();
697 PP_Var
ResourceRawVarData::CreatePPVar(PP_Instance instance
) {
698 // If this is not a pending resource host, just create the var.
699 if (pp_resource_
|| !creation_message_
) {
700 return PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVar(
704 // This is a pending resource host, so create the resource and var.
705 return PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVarFromMessage(
708 pending_renderer_host_id_
,
709 pending_browser_host_id_
);
712 void ResourceRawVarData::PopulatePPVar(const PP_Var
& var
,
713 const std::vector
<PP_Var
>& graph
) {
716 void ResourceRawVarData::Write(IPC::Message
* m
,
717 const HandleWriter
& handle_writer
) {
718 m
->WriteInt(static_cast<int>(pp_resource_
));
719 m
->WriteInt(pending_renderer_host_id_
);
720 m
->WriteInt(pending_browser_host_id_
);
721 m
->WriteBool(creation_message_
);
722 if (creation_message_
)
723 IPC::ParamTraits
<IPC::Message
>::Write(m
, *creation_message_
);
726 bool ResourceRawVarData::Read(PP_VarType type
,
727 const IPC::Message
* m
,
728 base::PickleIterator
* iter
) {
730 if (!iter
->ReadInt(&value
))
732 pp_resource_
= static_cast<PP_Resource
>(value
);
733 if (!iter
->ReadInt(&pending_renderer_host_id_
))
735 if (!iter
->ReadInt(&pending_browser_host_id_
))
737 bool has_creation_message
;
738 if (!iter
->ReadBool(&has_creation_message
))
740 if (has_creation_message
) {
741 creation_message_
.reset(new IPC::Message());
742 if (!IPC::ParamTraits
<IPC::Message
>::Read(m
, iter
, creation_message_
.get()))
745 creation_message_
.reset();