blob: 6d7ee285b2272944ba5a21f558154d8d5d38ecde [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: haberman@google.com (Josh Haberman)
32
33#include <google/protobuf/pyext/map_container.h>
34
Austin Schuh40c16522018-10-28 20:27:54 -070035#include <memory>
36
Brian Silverman9c614bc2016-02-15 20:20:02 -050037#include <google/protobuf/stubs/logging.h>
38#include <google/protobuf/stubs/common.h>
Brian Silverman9c614bc2016-02-15 20:20:02 -050039#include <google/protobuf/map_field.h>
40#include <google/protobuf/map.h>
41#include <google/protobuf/message.h>
Austin Schuh40c16522018-10-28 20:27:54 -070042#include <google/protobuf/pyext/message_factory.h>
Brian Silverman9c614bc2016-02-15 20:20:02 -050043#include <google/protobuf/pyext/message.h>
Austin Schuh40c16522018-10-28 20:27:54 -070044#include <google/protobuf/pyext/repeated_composite_container.h>
Brian Silverman9c614bc2016-02-15 20:20:02 -050045#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
46
47#if PY_MAJOR_VERSION >= 3
48 #define PyInt_FromLong PyLong_FromLong
49 #define PyInt_FromSize_t PyLong_FromSize_t
50#endif
51
52namespace google {
53namespace protobuf {
54namespace python {
55
56// Functions that need access to map reflection functionality.
57// They need to be contained in this class because it is friended.
58class MapReflectionFriend {
59 public:
60 // Methods that are in common between the map types.
61 static PyObject* Contains(PyObject* _self, PyObject* key);
62 static Py_ssize_t Length(PyObject* _self);
63 static PyObject* GetIterator(PyObject *_self);
64 static PyObject* IterNext(PyObject* _self);
65
66 // Methods that differ between the map types.
67 static PyObject* ScalarMapGetItem(PyObject* _self, PyObject* key);
68 static PyObject* MessageMapGetItem(PyObject* _self, PyObject* key);
69 static int ScalarMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
70 static int MessageMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
71};
72
73struct MapIterator {
74 PyObject_HEAD;
75
Austin Schuh40c16522018-10-28 20:27:54 -070076 std::unique_ptr<::google::protobuf::MapIterator> iter;
Brian Silverman9c614bc2016-02-15 20:20:02 -050077
78 // A pointer back to the container, so we can notice changes to the version.
79 // We own a ref on this.
80 MapContainer* container;
81
82 // We need to keep a ref on the Message* too, because
83 // MapIterator::~MapIterator() accesses it. Normally this would be ok because
84 // the ref on container (above) would guarantee outlive semantics. However in
85 // the case of ClearField(), InitializeAndCopyToParentContainer() resets the
86 // message pointer (and the owner) to a different message, a copy of the
87 // original. But our iterator still points to the original, which could now
88 // get deleted before us.
89 //
90 // To prevent this, we ensure that the Message will always stay alive as long
91 // as this iterator does. This is solely for the benefit of the MapIterator
92 // destructor -- we should never actually access the iterator in this state
93 // except to delete it.
Austin Schuh40c16522018-10-28 20:27:54 -070094 CMessage::OwnerRef owner;
Brian Silverman9c614bc2016-02-15 20:20:02 -050095
96 // The version of the map when we took the iterator to it.
97 //
98 // We store this so that if the map is modified during iteration we can throw
99 // an error.
100 uint64 version;
101
102 // True if the container is empty. We signal this separately to avoid calling
103 // any of the iteration methods, which are non-const.
104 bool empty;
105};
106
107Message* MapContainer::GetMutableMessage() {
108 cmessage::AssureWritable(parent);
109 return const_cast<Message*>(message);
110}
111
112// Consumes a reference on the Python string object.
113static bool PyStringToSTL(PyObject* py_string, string* stl_string) {
114 char *value;
115 Py_ssize_t value_len;
116
117 if (!py_string) {
118 return false;
119 }
120 if (PyBytes_AsStringAndSize(py_string, &value, &value_len) < 0) {
121 Py_DECREF(py_string);
122 return false;
123 } else {
124 stl_string->assign(value, value_len);
125 Py_DECREF(py_string);
126 return true;
127 }
128}
129
130static bool PythonToMapKey(PyObject* obj,
131 const FieldDescriptor* field_descriptor,
132 MapKey* key) {
133 switch (field_descriptor->cpp_type()) {
134 case FieldDescriptor::CPPTYPE_INT32: {
135 GOOGLE_CHECK_GET_INT32(obj, value, false);
136 key->SetInt32Value(value);
137 break;
138 }
139 case FieldDescriptor::CPPTYPE_INT64: {
140 GOOGLE_CHECK_GET_INT64(obj, value, false);
141 key->SetInt64Value(value);
142 break;
143 }
144 case FieldDescriptor::CPPTYPE_UINT32: {
145 GOOGLE_CHECK_GET_UINT32(obj, value, false);
146 key->SetUInt32Value(value);
147 break;
148 }
149 case FieldDescriptor::CPPTYPE_UINT64: {
150 GOOGLE_CHECK_GET_UINT64(obj, value, false);
151 key->SetUInt64Value(value);
152 break;
153 }
154 case FieldDescriptor::CPPTYPE_BOOL: {
155 GOOGLE_CHECK_GET_BOOL(obj, value, false);
156 key->SetBoolValue(value);
157 break;
158 }
159 case FieldDescriptor::CPPTYPE_STRING: {
160 string str;
161 if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
162 return false;
163 }
164 key->SetStringValue(str);
165 break;
166 }
167 default:
168 PyErr_Format(
169 PyExc_SystemError, "Type %d cannot be a map key",
170 field_descriptor->cpp_type());
171 return false;
172 }
173 return true;
174}
175
176static PyObject* MapKeyToPython(const FieldDescriptor* field_descriptor,
177 const MapKey& key) {
178 switch (field_descriptor->cpp_type()) {
179 case FieldDescriptor::CPPTYPE_INT32:
180 return PyInt_FromLong(key.GetInt32Value());
181 case FieldDescriptor::CPPTYPE_INT64:
182 return PyLong_FromLongLong(key.GetInt64Value());
183 case FieldDescriptor::CPPTYPE_UINT32:
184 return PyInt_FromSize_t(key.GetUInt32Value());
185 case FieldDescriptor::CPPTYPE_UINT64:
186 return PyLong_FromUnsignedLongLong(key.GetUInt64Value());
187 case FieldDescriptor::CPPTYPE_BOOL:
188 return PyBool_FromLong(key.GetBoolValue());
189 case FieldDescriptor::CPPTYPE_STRING:
190 return ToStringObject(field_descriptor, key.GetStringValue());
191 default:
192 PyErr_Format(
193 PyExc_SystemError, "Couldn't convert type %d to value",
194 field_descriptor->cpp_type());
195 return NULL;
196 }
197}
198
199// This is only used for ScalarMap, so we don't need to handle the
200// CPPTYPE_MESSAGE case.
201PyObject* MapValueRefToPython(const FieldDescriptor* field_descriptor,
202 MapValueRef* value) {
203 switch (field_descriptor->cpp_type()) {
204 case FieldDescriptor::CPPTYPE_INT32:
205 return PyInt_FromLong(value->GetInt32Value());
206 case FieldDescriptor::CPPTYPE_INT64:
207 return PyLong_FromLongLong(value->GetInt64Value());
208 case FieldDescriptor::CPPTYPE_UINT32:
209 return PyInt_FromSize_t(value->GetUInt32Value());
210 case FieldDescriptor::CPPTYPE_UINT64:
211 return PyLong_FromUnsignedLongLong(value->GetUInt64Value());
212 case FieldDescriptor::CPPTYPE_FLOAT:
213 return PyFloat_FromDouble(value->GetFloatValue());
214 case FieldDescriptor::CPPTYPE_DOUBLE:
215 return PyFloat_FromDouble(value->GetDoubleValue());
216 case FieldDescriptor::CPPTYPE_BOOL:
217 return PyBool_FromLong(value->GetBoolValue());
218 case FieldDescriptor::CPPTYPE_STRING:
219 return ToStringObject(field_descriptor, value->GetStringValue());
220 case FieldDescriptor::CPPTYPE_ENUM:
221 return PyInt_FromLong(value->GetEnumValue());
222 default:
223 PyErr_Format(
224 PyExc_SystemError, "Couldn't convert type %d to value",
225 field_descriptor->cpp_type());
226 return NULL;
227 }
228}
229
230// This is only used for ScalarMap, so we don't need to handle the
231// CPPTYPE_MESSAGE case.
232static bool PythonToMapValueRef(PyObject* obj,
233 const FieldDescriptor* field_descriptor,
234 bool allow_unknown_enum_values,
235 MapValueRef* value_ref) {
236 switch (field_descriptor->cpp_type()) {
237 case FieldDescriptor::CPPTYPE_INT32: {
238 GOOGLE_CHECK_GET_INT32(obj, value, false);
239 value_ref->SetInt32Value(value);
240 return true;
241 }
242 case FieldDescriptor::CPPTYPE_INT64: {
243 GOOGLE_CHECK_GET_INT64(obj, value, false);
244 value_ref->SetInt64Value(value);
245 return true;
246 }
247 case FieldDescriptor::CPPTYPE_UINT32: {
248 GOOGLE_CHECK_GET_UINT32(obj, value, false);
249 value_ref->SetUInt32Value(value);
250 return true;
251 }
252 case FieldDescriptor::CPPTYPE_UINT64: {
253 GOOGLE_CHECK_GET_UINT64(obj, value, false);
254 value_ref->SetUInt64Value(value);
255 return true;
256 }
257 case FieldDescriptor::CPPTYPE_FLOAT: {
258 GOOGLE_CHECK_GET_FLOAT(obj, value, false);
259 value_ref->SetFloatValue(value);
260 return true;
261 }
262 case FieldDescriptor::CPPTYPE_DOUBLE: {
263 GOOGLE_CHECK_GET_DOUBLE(obj, value, false);
264 value_ref->SetDoubleValue(value);
265 return true;
266 }
267 case FieldDescriptor::CPPTYPE_BOOL: {
268 GOOGLE_CHECK_GET_BOOL(obj, value, false);
269 value_ref->SetBoolValue(value);
270 return true;;
271 }
272 case FieldDescriptor::CPPTYPE_STRING: {
273 string str;
274 if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
275 return false;
276 }
277 value_ref->SetStringValue(str);
278 return true;
279 }
280 case FieldDescriptor::CPPTYPE_ENUM: {
281 GOOGLE_CHECK_GET_INT32(obj, value, false);
282 if (allow_unknown_enum_values) {
283 value_ref->SetEnumValue(value);
284 return true;
285 } else {
286 const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
287 const EnumValueDescriptor* enum_value =
288 enum_descriptor->FindValueByNumber(value);
289 if (enum_value != NULL) {
290 value_ref->SetEnumValue(value);
291 return true;
292 } else {
293 PyErr_Format(PyExc_ValueError, "Unknown enum value: %d", value);
294 return false;
295 }
296 }
297 break;
298 }
299 default:
300 PyErr_Format(
301 PyExc_SystemError, "Setting value to a field of unknown type %d",
302 field_descriptor->cpp_type());
303 return false;
304 }
305}
306
307// Map methods common to ScalarMap and MessageMap //////////////////////////////
308
309static MapContainer* GetMap(PyObject* obj) {
310 return reinterpret_cast<MapContainer*>(obj);
311}
312
313Py_ssize_t MapReflectionFriend::Length(PyObject* _self) {
314 MapContainer* self = GetMap(_self);
315 const google::protobuf::Message* message = self->message;
316 return message->GetReflection()->MapSize(*message,
317 self->parent_field_descriptor);
318}
319
320PyObject* Clear(PyObject* _self) {
321 MapContainer* self = GetMap(_self);
322 Message* message = self->GetMutableMessage();
323 const Reflection* reflection = message->GetReflection();
324
325 reflection->ClearField(message, self->parent_field_descriptor);
326
327 Py_RETURN_NONE;
328}
329
Austin Schuh40c16522018-10-28 20:27:54 -0700330PyObject* GetEntryClass(PyObject* _self) {
331 MapContainer* self = GetMap(_self);
332 CMessageClass* message_class = message_factory::GetMessageClass(
333 cmessage::GetFactoryForMessage(self->parent),
334 self->parent_field_descriptor->message_type());
335 Py_XINCREF(message_class);
336 return reinterpret_cast<PyObject*>(message_class);
337}
338
339PyObject* MergeFrom(PyObject* _self, PyObject* arg) {
340 MapContainer* self = GetMap(_self);
341 MapContainer* other_map = GetMap(arg);
342 Message* message = self->GetMutableMessage();
343 const Message* other_message = other_map->message;
344 const Reflection* reflection = message->GetReflection();
345 const Reflection* other_reflection = other_message->GetReflection();
346 int count = other_reflection->FieldSize(
347 *other_message, other_map->parent_field_descriptor);
348 for (int i = 0 ; i < count; i ++) {
349 reflection->AddMessage(message, self->parent_field_descriptor)->MergeFrom(
350 other_reflection->GetRepeatedMessage(
351 *other_message, other_map->parent_field_descriptor, i));
352 }
353 self->version++;
354 Py_RETURN_NONE;
355}
356
Brian Silverman9c614bc2016-02-15 20:20:02 -0500357PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
358 MapContainer* self = GetMap(_self);
359
360 const Message* message = self->message;
361 const Reflection* reflection = message->GetReflection();
362 MapKey map_key;
363
364 if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
365 return NULL;
366 }
367
368 if (reflection->ContainsMapKey(*message, self->parent_field_descriptor,
369 map_key)) {
370 Py_RETURN_TRUE;
371 } else {
372 Py_RETURN_FALSE;
373 }
374}
375
376// Initializes the underlying Message object of "to" so it becomes a new parent
Austin Schuh40c16522018-10-28 20:27:54 -0700377// map container, and copies all the values from "from" to it. A child map
Brian Silverman9c614bc2016-02-15 20:20:02 -0500378// container can be released by passing it as both from and to (e.g. making it
379// the recipient of the new parent message and copying the values from itself).
Austin Schuh40c16522018-10-28 20:27:54 -0700380// In fact, this is the only supported use at the moment.
Brian Silverman9c614bc2016-02-15 20:20:02 -0500381static int InitializeAndCopyToParentContainer(MapContainer* from,
382 MapContainer* to) {
383 // For now we require from == to, re-evaluate if we want to support deep copy
384 // as in repeated_scalar_container.cc.
385 GOOGLE_DCHECK(from == to);
386 Message* new_message = from->message->New();
387
388 if (MapReflectionFriend::Length(reinterpret_cast<PyObject*>(from)) > 0) {
389 // A somewhat roundabout way of copying just one field from old_message to
390 // new_message. This is the best we can do with what Reflection gives us.
391 Message* mutable_old = from->GetMutableMessage();
Austin Schuh40c16522018-10-28 20:27:54 -0700392 std::vector<const FieldDescriptor*> fields;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500393 fields.push_back(from->parent_field_descriptor);
394
395 // Move the map field into the new message.
396 mutable_old->GetReflection()->SwapFields(mutable_old, new_message, fields);
397
398 // If/when we support from != to, this will be required also to copy the
399 // map field back into the existing message:
400 // mutable_old->MergeFrom(*new_message);
401 }
402
403 // If from == to this could delete old_message.
404 to->owner.reset(new_message);
405
406 to->parent = NULL;
407 to->parent_field_descriptor = from->parent_field_descriptor;
408 to->message = new_message;
409
410 // Invalidate iterators, since they point to the old copy of the field.
411 to->version++;
412
413 return 0;
414}
415
416int MapContainer::Release() {
417 return InitializeAndCopyToParentContainer(this, this);
418}
419
420
421// ScalarMap ///////////////////////////////////////////////////////////////////
422
423PyObject *NewScalarMapContainer(
424 CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) {
425 if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
426 return NULL;
427 }
428
Austin Schuh40c16522018-10-28 20:27:54 -0700429 ScopedPyObjectPtr obj(PyType_GenericAlloc(ScalarMapContainer_Type, 0));
Brian Silverman9c614bc2016-02-15 20:20:02 -0500430 if (obj.get() == NULL) {
431 return PyErr_Format(PyExc_RuntimeError,
432 "Could not allocate new container.");
433 }
434
435 MapContainer* self = GetMap(obj.get());
436
437 self->message = parent->message;
438 self->parent = parent;
439 self->parent_field_descriptor = parent_field_descriptor;
440 self->owner = parent->owner;
441 self->version = 0;
442
443 self->key_field_descriptor =
444 parent_field_descriptor->message_type()->FindFieldByName("key");
445 self->value_field_descriptor =
446 parent_field_descriptor->message_type()->FindFieldByName("value");
447
448 if (self->key_field_descriptor == NULL ||
449 self->value_field_descriptor == NULL) {
450 return PyErr_Format(PyExc_KeyError,
451 "Map entry descriptor did not have key/value fields");
452 }
453
454 return obj.release();
455}
456
457PyObject* MapReflectionFriend::ScalarMapGetItem(PyObject* _self,
458 PyObject* key) {
459 MapContainer* self = GetMap(_self);
460
461 Message* message = self->GetMutableMessage();
462 const Reflection* reflection = message->GetReflection();
463 MapKey map_key;
464 MapValueRef value;
465
466 if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
467 return NULL;
468 }
469
470 if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
471 map_key, &value)) {
472 self->version++;
473 }
474
475 return MapValueRefToPython(self->value_field_descriptor, &value);
476}
477
478int MapReflectionFriend::ScalarMapSetItem(PyObject* _self, PyObject* key,
479 PyObject* v) {
480 MapContainer* self = GetMap(_self);
481
482 Message* message = self->GetMutableMessage();
483 const Reflection* reflection = message->GetReflection();
484 MapKey map_key;
485 MapValueRef value;
486
487 if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
488 return -1;
489 }
490
491 self->version++;
492
493 if (v) {
494 // Set item to v.
495 reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
496 map_key, &value);
497
498 return PythonToMapValueRef(v, self->value_field_descriptor,
499 reflection->SupportsUnknownEnumValues(), &value)
500 ? 0
501 : -1;
502 } else {
503 // Delete key from map.
504 if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
505 map_key)) {
506 return 0;
507 } else {
508 PyErr_Format(PyExc_KeyError, "Key not present in map");
509 return -1;
510 }
511 }
512}
513
514static PyObject* ScalarMapGet(PyObject* self, PyObject* args) {
515 PyObject* key;
516 PyObject* default_value = NULL;
517 if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
518 return NULL;
519 }
520
521 ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
522 if (is_present.get() == NULL) {
523 return NULL;
524 }
525
526 if (PyObject_IsTrue(is_present.get())) {
527 return MapReflectionFriend::ScalarMapGetItem(self, key);
528 } else {
529 if (default_value != NULL) {
530 Py_INCREF(default_value);
531 return default_value;
532 } else {
533 Py_RETURN_NONE;
534 }
535 }
536}
537
538static void ScalarMapDealloc(PyObject* _self) {
539 MapContainer* self = GetMap(_self);
540 self->owner.reset();
541 Py_TYPE(_self)->tp_free(_self);
542}
543
544static PyMethodDef ScalarMapMethods[] = {
545 { "__contains__", MapReflectionFriend::Contains, METH_O,
546 "Tests whether a key is a member of the map." },
547 { "clear", (PyCFunction)Clear, METH_NOARGS,
548 "Removes all elements from the map." },
549 { "get", ScalarMapGet, METH_VARARGS,
550 "Gets the value for the given key if present, or otherwise a default" },
Austin Schuh40c16522018-10-28 20:27:54 -0700551 { "GetEntryClass", (PyCFunction)GetEntryClass, METH_NOARGS,
552 "Return the class used to build Entries of (key, value) pairs." },
553 { "MergeFrom", (PyCFunction)MergeFrom, METH_O,
554 "Merges a map into the current map." },
Brian Silverman9c614bc2016-02-15 20:20:02 -0500555 /*
556 { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
557 "Makes a deep copy of the class." },
558 { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
559 "Outputs picklable representation of the repeated field." },
560 */
561 {NULL, NULL},
562};
563
Austin Schuh40c16522018-10-28 20:27:54 -0700564PyTypeObject *ScalarMapContainer_Type;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500565#if PY_MAJOR_VERSION >= 3
566 static PyType_Slot ScalarMapContainer_Type_slots[] = {
567 {Py_tp_dealloc, (void *)ScalarMapDealloc},
568 {Py_mp_length, (void *)MapReflectionFriend::Length},
569 {Py_mp_subscript, (void *)MapReflectionFriend::ScalarMapGetItem},
570 {Py_mp_ass_subscript, (void *)MapReflectionFriend::ScalarMapSetItem},
571 {Py_tp_methods, (void *)ScalarMapMethods},
572 {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
573 {0, 0},
574 };
575
576 PyType_Spec ScalarMapContainer_Type_spec = {
577 FULL_MODULE_NAME ".ScalarMapContainer",
578 sizeof(MapContainer),
579 0,
580 Py_TPFLAGS_DEFAULT,
581 ScalarMapContainer_Type_slots
582 };
Brian Silverman9c614bc2016-02-15 20:20:02 -0500583#else
584 static PyMappingMethods ScalarMapMappingMethods = {
585 MapReflectionFriend::Length, // mp_length
586 MapReflectionFriend::ScalarMapGetItem, // mp_subscript
587 MapReflectionFriend::ScalarMapSetItem, // mp_ass_subscript
588 };
589
Austin Schuh40c16522018-10-28 20:27:54 -0700590 PyTypeObject _ScalarMapContainer_Type = {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500591 PyVarObject_HEAD_INIT(&PyType_Type, 0)
592 FULL_MODULE_NAME ".ScalarMapContainer", // tp_name
593 sizeof(MapContainer), // tp_basicsize
594 0, // tp_itemsize
595 ScalarMapDealloc, // tp_dealloc
596 0, // tp_print
597 0, // tp_getattr
598 0, // tp_setattr
599 0, // tp_compare
600 0, // tp_repr
601 0, // tp_as_number
602 0, // tp_as_sequence
603 &ScalarMapMappingMethods, // tp_as_mapping
604 0, // tp_hash
605 0, // tp_call
606 0, // tp_str
607 0, // tp_getattro
608 0, // tp_setattro
609 0, // tp_as_buffer
610 Py_TPFLAGS_DEFAULT, // tp_flags
611 "A scalar map container", // tp_doc
612 0, // tp_traverse
613 0, // tp_clear
614 0, // tp_richcompare
615 0, // tp_weaklistoffset
616 MapReflectionFriend::GetIterator, // tp_iter
617 0, // tp_iternext
618 ScalarMapMethods, // tp_methods
619 0, // tp_members
620 0, // tp_getset
621 0, // tp_base
622 0, // tp_dict
623 0, // tp_descr_get
624 0, // tp_descr_set
625 0, // tp_dictoffset
626 0, // tp_init
627 };
628#endif
629
630
631// MessageMap //////////////////////////////////////////////////////////////////
632
633static MessageMapContainer* GetMessageMap(PyObject* obj) {
634 return reinterpret_cast<MessageMapContainer*>(obj);
635}
636
637static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
638 // Get or create the CMessage object corresponding to this message.
639 ScopedPyObjectPtr key(PyLong_FromVoidPtr(message));
640 PyObject* ret = PyDict_GetItem(self->message_dict, key.get());
641
642 if (ret == NULL) {
Austin Schuh40c16522018-10-28 20:27:54 -0700643 CMessage* cmsg = cmessage::NewEmptyMessage(self->message_class);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500644 ret = reinterpret_cast<PyObject*>(cmsg);
645
646 if (cmsg == NULL) {
647 return NULL;
648 }
649 cmsg->owner = self->owner;
650 cmsg->message = message;
651 cmsg->parent = self->parent;
652
653 if (PyDict_SetItem(self->message_dict, key.get(), ret) < 0) {
654 Py_DECREF(ret);
655 return NULL;
656 }
657 } else {
658 Py_INCREF(ret);
659 }
660
661 return ret;
662}
663
664PyObject* NewMessageMapContainer(
665 CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor,
Austin Schuh40c16522018-10-28 20:27:54 -0700666 CMessageClass* message_class) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500667 if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
668 return NULL;
669 }
670
Austin Schuh40c16522018-10-28 20:27:54 -0700671 PyObject* obj = PyType_GenericAlloc(MessageMapContainer_Type, 0);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500672 if (obj == NULL) {
673 return PyErr_Format(PyExc_RuntimeError,
674 "Could not allocate new container.");
675 }
676
677 MessageMapContainer* self = GetMessageMap(obj);
678
679 self->message = parent->message;
680 self->parent = parent;
681 self->parent_field_descriptor = parent_field_descriptor;
682 self->owner = parent->owner;
683 self->version = 0;
684
685 self->key_field_descriptor =
686 parent_field_descriptor->message_type()->FindFieldByName("key");
687 self->value_field_descriptor =
688 parent_field_descriptor->message_type()->FindFieldByName("value");
689
690 self->message_dict = PyDict_New();
691 if (self->message_dict == NULL) {
692 return PyErr_Format(PyExc_RuntimeError,
693 "Could not allocate message dict.");
694 }
695
Austin Schuh40c16522018-10-28 20:27:54 -0700696 Py_INCREF(message_class);
697 self->message_class = message_class;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500698
699 if (self->key_field_descriptor == NULL ||
700 self->value_field_descriptor == NULL) {
701 Py_DECREF(obj);
702 return PyErr_Format(PyExc_KeyError,
703 "Map entry descriptor did not have key/value fields");
704 }
705
706 return obj;
707}
708
709int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key,
710 PyObject* v) {
711 if (v) {
712 PyErr_Format(PyExc_ValueError,
713 "Direct assignment of submessage not allowed");
714 return -1;
715 }
716
717 // Now we know that this is a delete, not a set.
718
719 MessageMapContainer* self = GetMessageMap(_self);
720 Message* message = self->GetMutableMessage();
721 const Reflection* reflection = message->GetReflection();
722 MapKey map_key;
723 MapValueRef value;
724
725 self->version++;
726
727 if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
728 return -1;
729 }
730
731 // Delete key from map.
Austin Schuh40c16522018-10-28 20:27:54 -0700732 if (reflection->ContainsMapKey(*message, self->parent_field_descriptor,
Brian Silverman9c614bc2016-02-15 20:20:02 -0500733 map_key)) {
Austin Schuh40c16522018-10-28 20:27:54 -0700734 // Delete key from CMessage dict.
735 MapValueRef value;
736 reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
737 map_key, &value);
738 ScopedPyObjectPtr key(PyLong_FromVoidPtr(value.MutableMessageValue()));
739
740 PyObject* cmsg_value = PyDict_GetItem(self->message_dict, key.get());
741 if (cmsg_value) {
742 // Need to keep CMessage stay alive if it is still referenced after
743 // deletion. Makes a new message and swaps values into CMessage
744 // instead of just removing.
745 CMessage* cmsg = reinterpret_cast<CMessage*>(cmsg_value);
746 Message* msg = cmsg->message;
747 cmsg->owner.reset(msg->New());
748 cmsg->message = cmsg->owner.get();
749 cmsg->parent = NULL;
750 msg->GetReflection()->Swap(msg, cmsg->message);
751 if (PyDict_DelItem(self->message_dict, key.get()) < 0) {
752 return -1;
753 }
754 }
755
756 // Delete key from map.
757 reflection->DeleteMapValue(message, self->parent_field_descriptor,
758 map_key);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500759 return 0;
760 } else {
761 PyErr_Format(PyExc_KeyError, "Key not present in map");
762 return -1;
763 }
764}
765
766PyObject* MapReflectionFriend::MessageMapGetItem(PyObject* _self,
767 PyObject* key) {
768 MessageMapContainer* self = GetMessageMap(_self);
769
770 Message* message = self->GetMutableMessage();
771 const Reflection* reflection = message->GetReflection();
772 MapKey map_key;
773 MapValueRef value;
774
775 if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
776 return NULL;
777 }
778
779 if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
780 map_key, &value)) {
781 self->version++;
782 }
783
784 return GetCMessage(self, value.MutableMessageValue());
785}
786
787PyObject* MessageMapGet(PyObject* self, PyObject* args) {
788 PyObject* key;
789 PyObject* default_value = NULL;
790 if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
791 return NULL;
792 }
793
794 ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
795 if (is_present.get() == NULL) {
796 return NULL;
797 }
798
799 if (PyObject_IsTrue(is_present.get())) {
800 return MapReflectionFriend::MessageMapGetItem(self, key);
801 } else {
802 if (default_value != NULL) {
803 Py_INCREF(default_value);
804 return default_value;
805 } else {
806 Py_RETURN_NONE;
807 }
808 }
809}
810
811static void MessageMapDealloc(PyObject* _self) {
812 MessageMapContainer* self = GetMessageMap(_self);
813 self->owner.reset();
814 Py_DECREF(self->message_dict);
Austin Schuh40c16522018-10-28 20:27:54 -0700815 Py_DECREF(self->message_class);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500816 Py_TYPE(_self)->tp_free(_self);
817}
818
819static PyMethodDef MessageMapMethods[] = {
820 { "__contains__", (PyCFunction)MapReflectionFriend::Contains, METH_O,
821 "Tests whether the map contains this element."},
822 { "clear", (PyCFunction)Clear, METH_NOARGS,
823 "Removes all elements from the map."},
824 { "get", MessageMapGet, METH_VARARGS,
825 "Gets the value for the given key if present, or otherwise a default" },
826 { "get_or_create", MapReflectionFriend::MessageMapGetItem, METH_O,
827 "Alias for getitem, useful to make explicit that the map is mutated." },
Austin Schuh40c16522018-10-28 20:27:54 -0700828 { "GetEntryClass", (PyCFunction)GetEntryClass, METH_NOARGS,
829 "Return the class used to build Entries of (key, value) pairs." },
830 { "MergeFrom", (PyCFunction)MergeFrom, METH_O,
831 "Merges a map into the current map." },
Brian Silverman9c614bc2016-02-15 20:20:02 -0500832 /*
833 { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
834 "Makes a deep copy of the class." },
835 { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
836 "Outputs picklable representation of the repeated field." },
837 */
838 {NULL, NULL},
839};
840
Austin Schuh40c16522018-10-28 20:27:54 -0700841PyTypeObject *MessageMapContainer_Type;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500842#if PY_MAJOR_VERSION >= 3
843 static PyType_Slot MessageMapContainer_Type_slots[] = {
844 {Py_tp_dealloc, (void *)MessageMapDealloc},
845 {Py_mp_length, (void *)MapReflectionFriend::Length},
846 {Py_mp_subscript, (void *)MapReflectionFriend::MessageMapGetItem},
847 {Py_mp_ass_subscript, (void *)MapReflectionFriend::MessageMapSetItem},
848 {Py_tp_methods, (void *)MessageMapMethods},
849 {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
850 {0, 0}
851 };
852
853 PyType_Spec MessageMapContainer_Type_spec = {
854 FULL_MODULE_NAME ".MessageMapContainer",
855 sizeof(MessageMapContainer),
856 0,
857 Py_TPFLAGS_DEFAULT,
858 MessageMapContainer_Type_slots
859 };
Brian Silverman9c614bc2016-02-15 20:20:02 -0500860#else
861 static PyMappingMethods MessageMapMappingMethods = {
862 MapReflectionFriend::Length, // mp_length
863 MapReflectionFriend::MessageMapGetItem, // mp_subscript
864 MapReflectionFriend::MessageMapSetItem, // mp_ass_subscript
865 };
866
Austin Schuh40c16522018-10-28 20:27:54 -0700867 PyTypeObject _MessageMapContainer_Type = {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500868 PyVarObject_HEAD_INIT(&PyType_Type, 0)
869 FULL_MODULE_NAME ".MessageMapContainer", // tp_name
870 sizeof(MessageMapContainer), // tp_basicsize
871 0, // tp_itemsize
872 MessageMapDealloc, // tp_dealloc
873 0, // tp_print
874 0, // tp_getattr
875 0, // tp_setattr
876 0, // tp_compare
877 0, // tp_repr
878 0, // tp_as_number
879 0, // tp_as_sequence
880 &MessageMapMappingMethods, // tp_as_mapping
881 0, // tp_hash
882 0, // tp_call
883 0, // tp_str
884 0, // tp_getattro
885 0, // tp_setattro
886 0, // tp_as_buffer
887 Py_TPFLAGS_DEFAULT, // tp_flags
888 "A map container for message", // tp_doc
889 0, // tp_traverse
890 0, // tp_clear
891 0, // tp_richcompare
892 0, // tp_weaklistoffset
893 MapReflectionFriend::GetIterator, // tp_iter
894 0, // tp_iternext
895 MessageMapMethods, // tp_methods
896 0, // tp_members
897 0, // tp_getset
898 0, // tp_base
899 0, // tp_dict
900 0, // tp_descr_get
901 0, // tp_descr_set
902 0, // tp_dictoffset
903 0, // tp_init
904 };
905#endif
906
907// MapIterator /////////////////////////////////////////////////////////////////
908
909static MapIterator* GetIter(PyObject* obj) {
910 return reinterpret_cast<MapIterator*>(obj);
911}
912
913PyObject* MapReflectionFriend::GetIterator(PyObject *_self) {
914 MapContainer* self = GetMap(_self);
915
916 ScopedPyObjectPtr obj(PyType_GenericAlloc(&MapIterator_Type, 0));
917 if (obj == NULL) {
918 return PyErr_Format(PyExc_KeyError, "Could not allocate iterator");
919 }
920
921 MapIterator* iter = GetIter(obj.get());
922
923 Py_INCREF(self);
924 iter->container = self;
925 iter->version = self->version;
926 iter->owner = self->owner;
927
928 if (MapReflectionFriend::Length(_self) > 0) {
929 Message* message = self->GetMutableMessage();
930 const Reflection* reflection = message->GetReflection();
931
932 iter->iter.reset(new ::google::protobuf::MapIterator(
933 reflection->MapBegin(message, self->parent_field_descriptor)));
934 }
935
936 return obj.release();
937}
938
939PyObject* MapReflectionFriend::IterNext(PyObject* _self) {
940 MapIterator* self = GetIter(_self);
941
942 // This won't catch mutations to the map performed by MergeFrom(); no easy way
943 // to address that.
944 if (self->version != self->container->version) {
945 return PyErr_Format(PyExc_RuntimeError,
946 "Map modified during iteration.");
947 }
948
949 if (self->iter.get() == NULL) {
950 return NULL;
951 }
952
953 Message* message = self->container->GetMutableMessage();
954 const Reflection* reflection = message->GetReflection();
955
956 if (*self->iter ==
957 reflection->MapEnd(message, self->container->parent_field_descriptor)) {
958 return NULL;
959 }
960
961 PyObject* ret = MapKeyToPython(self->container->key_field_descriptor,
962 self->iter->GetKey());
963
964 ++(*self->iter);
965
966 return ret;
967}
968
969static void DeallocMapIterator(PyObject* _self) {
970 MapIterator* self = GetIter(_self);
971 self->iter.reset();
972 self->owner.reset();
973 Py_XDECREF(self->container);
974 Py_TYPE(_self)->tp_free(_self);
975}
976
977PyTypeObject MapIterator_Type = {
978 PyVarObject_HEAD_INIT(&PyType_Type, 0)
979 FULL_MODULE_NAME ".MapIterator", // tp_name
980 sizeof(MapIterator), // tp_basicsize
981 0, // tp_itemsize
982 DeallocMapIterator, // tp_dealloc
983 0, // tp_print
984 0, // tp_getattr
985 0, // tp_setattr
986 0, // tp_compare
987 0, // tp_repr
988 0, // tp_as_number
989 0, // tp_as_sequence
990 0, // tp_as_mapping
991 0, // tp_hash
992 0, // tp_call
993 0, // tp_str
994 0, // tp_getattro
995 0, // tp_setattro
996 0, // tp_as_buffer
997 Py_TPFLAGS_DEFAULT, // tp_flags
998 "A scalar map iterator", // tp_doc
999 0, // tp_traverse
1000 0, // tp_clear
1001 0, // tp_richcompare
1002 0, // tp_weaklistoffset
1003 PyObject_SelfIter, // tp_iter
1004 MapReflectionFriend::IterNext, // tp_iternext
1005 0, // tp_methods
1006 0, // tp_members
1007 0, // tp_getset
1008 0, // tp_base
1009 0, // tp_dict
1010 0, // tp_descr_get
1011 0, // tp_descr_set
1012 0, // tp_dictoffset
1013 0, // tp_init
1014};
1015
Austin Schuh40c16522018-10-28 20:27:54 -07001016bool InitMapContainers() {
1017 // ScalarMapContainer_Type derives from our MutableMapping type.
1018 ScopedPyObjectPtr containers(PyImport_ImportModule(
1019 "google.protobuf.internal.containers"));
1020 if (containers == NULL) {
1021 return false;
1022 }
1023
1024 ScopedPyObjectPtr mutable_mapping(
1025 PyObject_GetAttrString(containers.get(), "MutableMapping"));
1026 if (mutable_mapping == NULL) {
1027 return false;
1028 }
1029
1030 if (!PyObject_TypeCheck(mutable_mapping.get(), &PyType_Type)) {
1031 return false;
1032 }
1033
1034 Py_INCREF(mutable_mapping.get());
1035#if PY_MAJOR_VERSION >= 3
1036 PyObject* bases = PyTuple_New(1);
1037 PyTuple_SET_ITEM(bases, 0, mutable_mapping.get());
1038
1039 ScalarMapContainer_Type = reinterpret_cast<PyTypeObject*>(
1040 PyType_FromSpecWithBases(&ScalarMapContainer_Type_spec, bases));
1041#else
1042 _ScalarMapContainer_Type.tp_base =
1043 reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
1044
1045 if (PyType_Ready(&_ScalarMapContainer_Type) < 0) {
1046 return false;
1047 }
1048
1049 ScalarMapContainer_Type = &_ScalarMapContainer_Type;
1050#endif
1051
1052 if (PyType_Ready(&MapIterator_Type) < 0) {
1053 return false;
1054 }
1055
1056#if PY_MAJOR_VERSION >= 3
1057 MessageMapContainer_Type = reinterpret_cast<PyTypeObject*>(
1058 PyType_FromSpecWithBases(&MessageMapContainer_Type_spec, bases));
1059#else
1060 Py_INCREF(mutable_mapping.get());
1061 _MessageMapContainer_Type.tp_base =
1062 reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
1063
1064 if (PyType_Ready(&_MessageMapContainer_Type) < 0) {
1065 return false;
1066 }
1067
1068 MessageMapContainer_Type = &_MessageMapContainer_Type;
1069#endif
1070 return true;
1071}
1072
Brian Silverman9c614bc2016-02-15 20:20:02 -05001073} // namespace python
1074} // namespace protobuf
1075} // namespace google