Index: third_party/protobuf/python/google/protobuf/pyext/map_container.cc |
diff --git a/third_party/protobuf/python/google/protobuf/pyext/map_container.cc b/third_party/protobuf/python/google/protobuf/pyext/map_container.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c39f7b839599ab804d1eb315b82115dc951b4e53 |
--- /dev/null |
+++ b/third_party/protobuf/python/google/protobuf/pyext/map_container.cc |
@@ -0,0 +1,912 @@ |
+// Protocol Buffers - Google's data interchange format |
+// Copyright 2008 Google Inc. All rights reserved. |
+// https://developers.google.com/protocol-buffers/ |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+// Author: haberman@google.com (Josh Haberman) |
+ |
+#include <google/protobuf/pyext/map_container.h> |
+ |
+#include <google/protobuf/stubs/logging.h> |
+#include <google/protobuf/stubs/common.h> |
+#include <google/protobuf/stubs/scoped_ptr.h> |
+#include <google/protobuf/map_field.h> |
+#include <google/protobuf/map.h> |
+#include <google/protobuf/message.h> |
+#include <google/protobuf/pyext/message.h> |
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h> |
+ |
+#if PY_MAJOR_VERSION >= 3 |
+ #define PyInt_FromLong PyLong_FromLong |
+ #define PyInt_FromSize_t PyLong_FromSize_t |
+#endif |
+ |
+namespace google { |
+namespace protobuf { |
+namespace python { |
+ |
+// Functions that need access to map reflection functionality. |
+// They need to be contained in this class because it is friended. |
+class MapReflectionFriend { |
+ public: |
+ // Methods that are in common between the map types. |
+ static PyObject* Contains(PyObject* _self, PyObject* key); |
+ static Py_ssize_t Length(PyObject* _self); |
+ static PyObject* GetIterator(PyObject *_self); |
+ static PyObject* IterNext(PyObject* _self); |
+ |
+ // Methods that differ between the map types. |
+ static PyObject* ScalarMapGetItem(PyObject* _self, PyObject* key); |
+ static PyObject* MessageMapGetItem(PyObject* _self, PyObject* key); |
+ static int ScalarMapSetItem(PyObject* _self, PyObject* key, PyObject* v); |
+ static int MessageMapSetItem(PyObject* _self, PyObject* key, PyObject* v); |
+}; |
+ |
+struct MapIterator { |
+ PyObject_HEAD; |
+ |
+ scoped_ptr< ::google::protobuf::MapIterator> iter; |
+ |
+ // A pointer back to the container, so we can notice changes to the version. |
+ // We own a ref on this. |
+ MapContainer* container; |
+ |
+ // We need to keep a ref on the Message* too, because |
+ // MapIterator::~MapIterator() accesses it. Normally this would be ok because |
+ // the ref on container (above) would guarantee outlive semantics. However in |
+ // the case of ClearField(), InitializeAndCopyToParentContainer() resets the |
+ // message pointer (and the owner) to a different message, a copy of the |
+ // original. But our iterator still points to the original, which could now |
+ // get deleted before us. |
+ // |
+ // To prevent this, we ensure that the Message will always stay alive as long |
+ // as this iterator does. This is solely for the benefit of the MapIterator |
+ // destructor -- we should never actually access the iterator in this state |
+ // except to delete it. |
+ shared_ptr<Message> owner; |
+ |
+ // The version of the map when we took the iterator to it. |
+ // |
+ // We store this so that if the map is modified during iteration we can throw |
+ // an error. |
+ uint64 version; |
+ |
+ // True if the container is empty. We signal this separately to avoid calling |
+ // any of the iteration methods, which are non-const. |
+ bool empty; |
+}; |
+ |
+Message* MapContainer::GetMutableMessage() { |
+ cmessage::AssureWritable(parent); |
+ return const_cast<Message*>(message); |
+} |
+ |
+// Consumes a reference on the Python string object. |
+static bool PyStringToSTL(PyObject* py_string, string* stl_string) { |
+ char *value; |
+ Py_ssize_t value_len; |
+ |
+ if (!py_string) { |
+ return false; |
+ } |
+ if (PyBytes_AsStringAndSize(py_string, &value, &value_len) < 0) { |
+ Py_DECREF(py_string); |
+ return false; |
+ } else { |
+ stl_string->assign(value, value_len); |
+ Py_DECREF(py_string); |
+ return true; |
+ } |
+} |
+ |
+static bool PythonToMapKey(PyObject* obj, |
+ const FieldDescriptor* field_descriptor, |
+ MapKey* key) { |
+ switch (field_descriptor->cpp_type()) { |
+ case FieldDescriptor::CPPTYPE_INT32: { |
+ GOOGLE_CHECK_GET_INT32(obj, value, false); |
+ key->SetInt32Value(value); |
+ break; |
+ } |
+ case FieldDescriptor::CPPTYPE_INT64: { |
+ GOOGLE_CHECK_GET_INT64(obj, value, false); |
+ key->SetInt64Value(value); |
+ break; |
+ } |
+ case FieldDescriptor::CPPTYPE_UINT32: { |
+ GOOGLE_CHECK_GET_UINT32(obj, value, false); |
+ key->SetUInt32Value(value); |
+ break; |
+ } |
+ case FieldDescriptor::CPPTYPE_UINT64: { |
+ GOOGLE_CHECK_GET_UINT64(obj, value, false); |
+ key->SetUInt64Value(value); |
+ break; |
+ } |
+ case FieldDescriptor::CPPTYPE_BOOL: { |
+ GOOGLE_CHECK_GET_BOOL(obj, value, false); |
+ key->SetBoolValue(value); |
+ break; |
+ } |
+ case FieldDescriptor::CPPTYPE_STRING: { |
+ string str; |
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) { |
+ return false; |
+ } |
+ key->SetStringValue(str); |
+ break; |
+ } |
+ default: |
+ PyErr_Format( |
+ PyExc_SystemError, "Type %d cannot be a map key", |
+ field_descriptor->cpp_type()); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+static PyObject* MapKeyToPython(const FieldDescriptor* field_descriptor, |
+ const MapKey& key) { |
+ switch (field_descriptor->cpp_type()) { |
+ case FieldDescriptor::CPPTYPE_INT32: |
+ return PyInt_FromLong(key.GetInt32Value()); |
+ case FieldDescriptor::CPPTYPE_INT64: |
+ return PyLong_FromLongLong(key.GetInt64Value()); |
+ case FieldDescriptor::CPPTYPE_UINT32: |
+ return PyInt_FromSize_t(key.GetUInt32Value()); |
+ case FieldDescriptor::CPPTYPE_UINT64: |
+ return PyLong_FromUnsignedLongLong(key.GetUInt64Value()); |
+ case FieldDescriptor::CPPTYPE_BOOL: |
+ return PyBool_FromLong(key.GetBoolValue()); |
+ case FieldDescriptor::CPPTYPE_STRING: |
+ return ToStringObject(field_descriptor, key.GetStringValue()); |
+ default: |
+ PyErr_Format( |
+ PyExc_SystemError, "Couldn't convert type %d to value", |
+ field_descriptor->cpp_type()); |
+ return NULL; |
+ } |
+} |
+ |
+// This is only used for ScalarMap, so we don't need to handle the |
+// CPPTYPE_MESSAGE case. |
+PyObject* MapValueRefToPython(const FieldDescriptor* field_descriptor, |
+ MapValueRef* value) { |
+ switch (field_descriptor->cpp_type()) { |
+ case FieldDescriptor::CPPTYPE_INT32: |
+ return PyInt_FromLong(value->GetInt32Value()); |
+ case FieldDescriptor::CPPTYPE_INT64: |
+ return PyLong_FromLongLong(value->GetInt64Value()); |
+ case FieldDescriptor::CPPTYPE_UINT32: |
+ return PyInt_FromSize_t(value->GetUInt32Value()); |
+ case FieldDescriptor::CPPTYPE_UINT64: |
+ return PyLong_FromUnsignedLongLong(value->GetUInt64Value()); |
+ case FieldDescriptor::CPPTYPE_FLOAT: |
+ return PyFloat_FromDouble(value->GetFloatValue()); |
+ case FieldDescriptor::CPPTYPE_DOUBLE: |
+ return PyFloat_FromDouble(value->GetDoubleValue()); |
+ case FieldDescriptor::CPPTYPE_BOOL: |
+ return PyBool_FromLong(value->GetBoolValue()); |
+ case FieldDescriptor::CPPTYPE_STRING: |
+ return ToStringObject(field_descriptor, value->GetStringValue()); |
+ case FieldDescriptor::CPPTYPE_ENUM: |
+ return PyInt_FromLong(value->GetEnumValue()); |
+ default: |
+ PyErr_Format( |
+ PyExc_SystemError, "Couldn't convert type %d to value", |
+ field_descriptor->cpp_type()); |
+ return NULL; |
+ } |
+} |
+ |
+// This is only used for ScalarMap, so we don't need to handle the |
+// CPPTYPE_MESSAGE case. |
+static bool PythonToMapValueRef(PyObject* obj, |
+ const FieldDescriptor* field_descriptor, |
+ bool allow_unknown_enum_values, |
+ MapValueRef* value_ref) { |
+ switch (field_descriptor->cpp_type()) { |
+ case FieldDescriptor::CPPTYPE_INT32: { |
+ GOOGLE_CHECK_GET_INT32(obj, value, false); |
+ value_ref->SetInt32Value(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_INT64: { |
+ GOOGLE_CHECK_GET_INT64(obj, value, false); |
+ value_ref->SetInt64Value(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_UINT32: { |
+ GOOGLE_CHECK_GET_UINT32(obj, value, false); |
+ value_ref->SetUInt32Value(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_UINT64: { |
+ GOOGLE_CHECK_GET_UINT64(obj, value, false); |
+ value_ref->SetUInt64Value(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_FLOAT: { |
+ GOOGLE_CHECK_GET_FLOAT(obj, value, false); |
+ value_ref->SetFloatValue(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_DOUBLE: { |
+ GOOGLE_CHECK_GET_DOUBLE(obj, value, false); |
+ value_ref->SetDoubleValue(value); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_BOOL: { |
+ GOOGLE_CHECK_GET_BOOL(obj, value, false); |
+ value_ref->SetBoolValue(value); |
+ return true;; |
+ } |
+ case FieldDescriptor::CPPTYPE_STRING: { |
+ string str; |
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) { |
+ return false; |
+ } |
+ value_ref->SetStringValue(str); |
+ return true; |
+ } |
+ case FieldDescriptor::CPPTYPE_ENUM: { |
+ GOOGLE_CHECK_GET_INT32(obj, value, false); |
+ if (allow_unknown_enum_values) { |
+ value_ref->SetEnumValue(value); |
+ return true; |
+ } else { |
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type(); |
+ const EnumValueDescriptor* enum_value = |
+ enum_descriptor->FindValueByNumber(value); |
+ if (enum_value != NULL) { |
+ value_ref->SetEnumValue(value); |
+ return true; |
+ } else { |
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %d", value); |
+ return false; |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ PyErr_Format( |
+ PyExc_SystemError, "Setting value to a field of unknown type %d", |
+ field_descriptor->cpp_type()); |
+ return false; |
+ } |
+} |
+ |
+// Map methods common to ScalarMap and MessageMap ////////////////////////////// |
+ |
+static MapContainer* GetMap(PyObject* obj) { |
+ return reinterpret_cast<MapContainer*>(obj); |
+} |
+ |
+Py_ssize_t MapReflectionFriend::Length(PyObject* _self) { |
+ MapContainer* self = GetMap(_self); |
+ const google::protobuf::Message* message = self->message; |
+ return message->GetReflection()->MapSize(*message, |
+ self->parent_field_descriptor); |
+} |
+ |
+PyObject* Clear(PyObject* _self) { |
+ MapContainer* self = GetMap(_self); |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ |
+ reflection->ClearField(message, self->parent_field_descriptor); |
+ |
+ Py_RETURN_NONE; |
+} |
+ |
+PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) { |
+ MapContainer* self = GetMap(_self); |
+ |
+ const Message* message = self->message; |
+ const Reflection* reflection = message->GetReflection(); |
+ MapKey map_key; |
+ |
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) { |
+ return NULL; |
+ } |
+ |
+ if (reflection->ContainsMapKey(*message, self->parent_field_descriptor, |
+ map_key)) { |
+ Py_RETURN_TRUE; |
+ } else { |
+ Py_RETURN_FALSE; |
+ } |
+} |
+ |
+// Initializes the underlying Message object of "to" so it becomes a new parent |
+// repeated scalar, and copies all the values from "from" to it. A child scalar |
+// container can be released by passing it as both from and to (e.g. making it |
+// the recipient of the new parent message and copying the values from itself). |
+static int InitializeAndCopyToParentContainer(MapContainer* from, |
+ MapContainer* to) { |
+ // For now we require from == to, re-evaluate if we want to support deep copy |
+ // as in repeated_scalar_container.cc. |
+ GOOGLE_DCHECK(from == to); |
+ Message* new_message = from->message->New(); |
+ |
+ if (MapReflectionFriend::Length(reinterpret_cast<PyObject*>(from)) > 0) { |
+ // A somewhat roundabout way of copying just one field from old_message to |
+ // new_message. This is the best we can do with what Reflection gives us. |
+ Message* mutable_old = from->GetMutableMessage(); |
+ vector<const FieldDescriptor*> fields; |
+ fields.push_back(from->parent_field_descriptor); |
+ |
+ // Move the map field into the new message. |
+ mutable_old->GetReflection()->SwapFields(mutable_old, new_message, fields); |
+ |
+ // If/when we support from != to, this will be required also to copy the |
+ // map field back into the existing message: |
+ // mutable_old->MergeFrom(*new_message); |
+ } |
+ |
+ // If from == to this could delete old_message. |
+ to->owner.reset(new_message); |
+ |
+ to->parent = NULL; |
+ to->parent_field_descriptor = from->parent_field_descriptor; |
+ to->message = new_message; |
+ |
+ // Invalidate iterators, since they point to the old copy of the field. |
+ to->version++; |
+ |
+ return 0; |
+} |
+ |
+int MapContainer::Release() { |
+ return InitializeAndCopyToParentContainer(this, this); |
+} |
+ |
+ |
+// ScalarMap /////////////////////////////////////////////////////////////////// |
+ |
+PyObject *NewScalarMapContainer( |
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) { |
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { |
+ return NULL; |
+ } |
+ |
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0)); |
+ if (obj.get() == NULL) { |
+ return PyErr_Format(PyExc_RuntimeError, |
+ "Could not allocate new container."); |
+ } |
+ |
+ MapContainer* self = GetMap(obj.get()); |
+ |
+ self->message = parent->message; |
+ self->parent = parent; |
+ self->parent_field_descriptor = parent_field_descriptor; |
+ self->owner = parent->owner; |
+ self->version = 0; |
+ |
+ self->key_field_descriptor = |
+ parent_field_descriptor->message_type()->FindFieldByName("key"); |
+ self->value_field_descriptor = |
+ parent_field_descriptor->message_type()->FindFieldByName("value"); |
+ |
+ if (self->key_field_descriptor == NULL || |
+ self->value_field_descriptor == NULL) { |
+ return PyErr_Format(PyExc_KeyError, |
+ "Map entry descriptor did not have key/value fields"); |
+ } |
+ |
+ return obj.release(); |
+} |
+ |
+PyObject* MapReflectionFriend::ScalarMapGetItem(PyObject* _self, |
+ PyObject* key) { |
+ MapContainer* self = GetMap(_self); |
+ |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ MapKey map_key; |
+ MapValueRef value; |
+ |
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) { |
+ return NULL; |
+ } |
+ |
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor, |
+ map_key, &value)) { |
+ self->version++; |
+ } |
+ |
+ return MapValueRefToPython(self->value_field_descriptor, &value); |
+} |
+ |
+int MapReflectionFriend::ScalarMapSetItem(PyObject* _self, PyObject* key, |
+ PyObject* v) { |
+ MapContainer* self = GetMap(_self); |
+ |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ MapKey map_key; |
+ MapValueRef value; |
+ |
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) { |
+ return -1; |
+ } |
+ |
+ self->version++; |
+ |
+ if (v) { |
+ // Set item to v. |
+ reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor, |
+ map_key, &value); |
+ |
+ return PythonToMapValueRef(v, self->value_field_descriptor, |
+ reflection->SupportsUnknownEnumValues(), &value) |
+ ? 0 |
+ : -1; |
+ } else { |
+ // Delete key from map. |
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor, |
+ map_key)) { |
+ return 0; |
+ } else { |
+ PyErr_Format(PyExc_KeyError, "Key not present in map"); |
+ return -1; |
+ } |
+ } |
+} |
+ |
+static PyObject* ScalarMapGet(PyObject* self, PyObject* args) { |
+ PyObject* key; |
+ PyObject* default_value = NULL; |
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { |
+ return NULL; |
+ } |
+ |
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key)); |
+ if (is_present.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ if (PyObject_IsTrue(is_present.get())) { |
+ return MapReflectionFriend::ScalarMapGetItem(self, key); |
+ } else { |
+ if (default_value != NULL) { |
+ Py_INCREF(default_value); |
+ return default_value; |
+ } else { |
+ Py_RETURN_NONE; |
+ } |
+ } |
+} |
+ |
+static void ScalarMapDealloc(PyObject* _self) { |
+ MapContainer* self = GetMap(_self); |
+ self->owner.reset(); |
+ Py_TYPE(_self)->tp_free(_self); |
+} |
+ |
+static PyMappingMethods ScalarMapMappingMethods = { |
+ MapReflectionFriend::Length, // mp_length |
+ MapReflectionFriend::ScalarMapGetItem, // mp_subscript |
+ MapReflectionFriend::ScalarMapSetItem, // mp_ass_subscript |
+}; |
+ |
+static PyMethodDef ScalarMapMethods[] = { |
+ { "__contains__", MapReflectionFriend::Contains, METH_O, |
+ "Tests whether a key is a member of the map." }, |
+ { "clear", (PyCFunction)Clear, METH_NOARGS, |
+ "Removes all elements from the map." }, |
+ { "get", ScalarMapGet, METH_VARARGS, |
+ "Gets the value for the given key if present, or otherwise a default" }, |
+ /* |
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, |
+ "Makes a deep copy of the class." }, |
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, |
+ "Outputs picklable representation of the repeated field." }, |
+ */ |
+ {NULL, NULL}, |
+}; |
+ |
+PyTypeObject ScalarMapContainer_Type = { |
+ PyVarObject_HEAD_INIT(&PyType_Type, 0) |
+ FULL_MODULE_NAME ".ScalarMapContainer", // tp_name |
+ sizeof(MapContainer), // tp_basicsize |
+ 0, // tp_itemsize |
+ ScalarMapDealloc, // tp_dealloc |
+ 0, // tp_print |
+ 0, // tp_getattr |
+ 0, // tp_setattr |
+ 0, // tp_compare |
+ 0, // tp_repr |
+ 0, // tp_as_number |
+ 0, // tp_as_sequence |
+ &ScalarMapMappingMethods, // tp_as_mapping |
+ 0, // tp_hash |
+ 0, // tp_call |
+ 0, // tp_str |
+ 0, // tp_getattro |
+ 0, // tp_setattro |
+ 0, // tp_as_buffer |
+ Py_TPFLAGS_DEFAULT, // tp_flags |
+ "A scalar map container", // tp_doc |
+ 0, // tp_traverse |
+ 0, // tp_clear |
+ 0, // tp_richcompare |
+ 0, // tp_weaklistoffset |
+ MapReflectionFriend::GetIterator, // tp_iter |
+ 0, // tp_iternext |
+ ScalarMapMethods, // tp_methods |
+ 0, // tp_members |
+ 0, // tp_getset |
+ 0, // tp_base |
+ 0, // tp_dict |
+ 0, // tp_descr_get |
+ 0, // tp_descr_set |
+ 0, // tp_dictoffset |
+ 0, // tp_init |
+}; |
+ |
+ |
+// MessageMap ////////////////////////////////////////////////////////////////// |
+ |
+static MessageMapContainer* GetMessageMap(PyObject* obj) { |
+ return reinterpret_cast<MessageMapContainer*>(obj); |
+} |
+ |
+static PyObject* GetCMessage(MessageMapContainer* self, Message* message) { |
+ // Get or create the CMessage object corresponding to this message. |
+ ScopedPyObjectPtr key(PyLong_FromVoidPtr(message)); |
+ PyObject* ret = PyDict_GetItem(self->message_dict, key.get()); |
+ |
+ if (ret == NULL) { |
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, |
+ message->GetDescriptor()); |
+ ret = reinterpret_cast<PyObject*>(cmsg); |
+ |
+ if (cmsg == NULL) { |
+ return NULL; |
+ } |
+ cmsg->owner = self->owner; |
+ cmsg->message = message; |
+ cmsg->parent = self->parent; |
+ |
+ if (PyDict_SetItem(self->message_dict, key.get(), ret) < 0) { |
+ Py_DECREF(ret); |
+ return NULL; |
+ } |
+ } else { |
+ Py_INCREF(ret); |
+ } |
+ |
+ return ret; |
+} |
+ |
+PyObject* NewMessageMapContainer( |
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor, |
+ PyObject* concrete_class) { |
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { |
+ return NULL; |
+ } |
+ |
+ PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0); |
+ if (obj == NULL) { |
+ return PyErr_Format(PyExc_RuntimeError, |
+ "Could not allocate new container."); |
+ } |
+ |
+ MessageMapContainer* self = GetMessageMap(obj); |
+ |
+ self->message = parent->message; |
+ self->parent = parent; |
+ self->parent_field_descriptor = parent_field_descriptor; |
+ self->owner = parent->owner; |
+ self->version = 0; |
+ |
+ self->key_field_descriptor = |
+ parent_field_descriptor->message_type()->FindFieldByName("key"); |
+ self->value_field_descriptor = |
+ parent_field_descriptor->message_type()->FindFieldByName("value"); |
+ |
+ self->message_dict = PyDict_New(); |
+ if (self->message_dict == NULL) { |
+ return PyErr_Format(PyExc_RuntimeError, |
+ "Could not allocate message dict."); |
+ } |
+ |
+ Py_INCREF(concrete_class); |
+ self->subclass_init = concrete_class; |
+ |
+ if (self->key_field_descriptor == NULL || |
+ self->value_field_descriptor == NULL) { |
+ Py_DECREF(obj); |
+ return PyErr_Format(PyExc_KeyError, |
+ "Map entry descriptor did not have key/value fields"); |
+ } |
+ |
+ return obj; |
+} |
+ |
+int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key, |
+ PyObject* v) { |
+ if (v) { |
+ PyErr_Format(PyExc_ValueError, |
+ "Direct assignment of submessage not allowed"); |
+ return -1; |
+ } |
+ |
+ // Now we know that this is a delete, not a set. |
+ |
+ MessageMapContainer* self = GetMessageMap(_self); |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ MapKey map_key; |
+ MapValueRef value; |
+ |
+ self->version++; |
+ |
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) { |
+ return -1; |
+ } |
+ |
+ // Delete key from map. |
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor, |
+ map_key)) { |
+ return 0; |
+ } else { |
+ PyErr_Format(PyExc_KeyError, "Key not present in map"); |
+ return -1; |
+ } |
+} |
+ |
+PyObject* MapReflectionFriend::MessageMapGetItem(PyObject* _self, |
+ PyObject* key) { |
+ MessageMapContainer* self = GetMessageMap(_self); |
+ |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ MapKey map_key; |
+ MapValueRef value; |
+ |
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) { |
+ return NULL; |
+ } |
+ |
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor, |
+ map_key, &value)) { |
+ self->version++; |
+ } |
+ |
+ return GetCMessage(self, value.MutableMessageValue()); |
+} |
+ |
+PyObject* MessageMapGet(PyObject* self, PyObject* args) { |
+ PyObject* key; |
+ PyObject* default_value = NULL; |
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { |
+ return NULL; |
+ } |
+ |
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key)); |
+ if (is_present.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ if (PyObject_IsTrue(is_present.get())) { |
+ return MapReflectionFriend::MessageMapGetItem(self, key); |
+ } else { |
+ if (default_value != NULL) { |
+ Py_INCREF(default_value); |
+ return default_value; |
+ } else { |
+ Py_RETURN_NONE; |
+ } |
+ } |
+} |
+ |
+static void MessageMapDealloc(PyObject* _self) { |
+ MessageMapContainer* self = GetMessageMap(_self); |
+ self->owner.reset(); |
+ Py_DECREF(self->message_dict); |
+ Py_TYPE(_self)->tp_free(_self); |
+} |
+ |
+static PyMappingMethods MessageMapMappingMethods = { |
+ MapReflectionFriend::Length, // mp_length |
+ MapReflectionFriend::MessageMapGetItem, // mp_subscript |
+ MapReflectionFriend::MessageMapSetItem, // mp_ass_subscript |
+}; |
+ |
+static PyMethodDef MessageMapMethods[] = { |
+ { "__contains__", (PyCFunction)MapReflectionFriend::Contains, METH_O, |
+ "Tests whether the map contains this element."}, |
+ { "clear", (PyCFunction)Clear, METH_NOARGS, |
+ "Removes all elements from the map."}, |
+ { "get", MessageMapGet, METH_VARARGS, |
+ "Gets the value for the given key if present, or otherwise a default" }, |
+ { "get_or_create", MapReflectionFriend::MessageMapGetItem, METH_O, |
+ "Alias for getitem, useful to make explicit that the map is mutated." }, |
+ /* |
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, |
+ "Makes a deep copy of the class." }, |
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, |
+ "Outputs picklable representation of the repeated field." }, |
+ */ |
+ {NULL, NULL}, |
+}; |
+ |
+PyTypeObject MessageMapContainer_Type = { |
+ PyVarObject_HEAD_INIT(&PyType_Type, 0) |
+ FULL_MODULE_NAME ".MessageMapContainer", // tp_name |
+ sizeof(MessageMapContainer), // tp_basicsize |
+ 0, // tp_itemsize |
+ MessageMapDealloc, // tp_dealloc |
+ 0, // tp_print |
+ 0, // tp_getattr |
+ 0, // tp_setattr |
+ 0, // tp_compare |
+ 0, // tp_repr |
+ 0, // tp_as_number |
+ 0, // tp_as_sequence |
+ &MessageMapMappingMethods, // tp_as_mapping |
+ 0, // tp_hash |
+ 0, // tp_call |
+ 0, // tp_str |
+ 0, // tp_getattro |
+ 0, // tp_setattro |
+ 0, // tp_as_buffer |
+ Py_TPFLAGS_DEFAULT, // tp_flags |
+ "A map container for message", // tp_doc |
+ 0, // tp_traverse |
+ 0, // tp_clear |
+ 0, // tp_richcompare |
+ 0, // tp_weaklistoffset |
+ MapReflectionFriend::GetIterator, // tp_iter |
+ 0, // tp_iternext |
+ MessageMapMethods, // tp_methods |
+ 0, // tp_members |
+ 0, // tp_getset |
+ 0, // tp_base |
+ 0, // tp_dict |
+ 0, // tp_descr_get |
+ 0, // tp_descr_set |
+ 0, // tp_dictoffset |
+ 0, // tp_init |
+}; |
+ |
+// MapIterator ///////////////////////////////////////////////////////////////// |
+ |
+static MapIterator* GetIter(PyObject* obj) { |
+ return reinterpret_cast<MapIterator*>(obj); |
+} |
+ |
+PyObject* MapReflectionFriend::GetIterator(PyObject *_self) { |
+ MapContainer* self = GetMap(_self); |
+ |
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&MapIterator_Type, 0)); |
+ if (obj == NULL) { |
+ return PyErr_Format(PyExc_KeyError, "Could not allocate iterator"); |
+ } |
+ |
+ MapIterator* iter = GetIter(obj.get()); |
+ |
+ Py_INCREF(self); |
+ iter->container = self; |
+ iter->version = self->version; |
+ iter->owner = self->owner; |
+ |
+ if (MapReflectionFriend::Length(_self) > 0) { |
+ Message* message = self->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ |
+ iter->iter.reset(new ::google::protobuf::MapIterator( |
+ reflection->MapBegin(message, self->parent_field_descriptor))); |
+ } |
+ |
+ return obj.release(); |
+} |
+ |
+PyObject* MapReflectionFriend::IterNext(PyObject* _self) { |
+ MapIterator* self = GetIter(_self); |
+ |
+ // This won't catch mutations to the map performed by MergeFrom(); no easy way |
+ // to address that. |
+ if (self->version != self->container->version) { |
+ return PyErr_Format(PyExc_RuntimeError, |
+ "Map modified during iteration."); |
+ } |
+ |
+ if (self->iter.get() == NULL) { |
+ return NULL; |
+ } |
+ |
+ Message* message = self->container->GetMutableMessage(); |
+ const Reflection* reflection = message->GetReflection(); |
+ |
+ if (*self->iter == |
+ reflection->MapEnd(message, self->container->parent_field_descriptor)) { |
+ return NULL; |
+ } |
+ |
+ PyObject* ret = MapKeyToPython(self->container->key_field_descriptor, |
+ self->iter->GetKey()); |
+ |
+ ++(*self->iter); |
+ |
+ return ret; |
+} |
+ |
+static void DeallocMapIterator(PyObject* _self) { |
+ MapIterator* self = GetIter(_self); |
+ self->iter.reset(); |
+ self->owner.reset(); |
+ Py_XDECREF(self->container); |
+ Py_TYPE(_self)->tp_free(_self); |
+} |
+ |
+PyTypeObject MapIterator_Type = { |
+ PyVarObject_HEAD_INIT(&PyType_Type, 0) |
+ FULL_MODULE_NAME ".MapIterator", // tp_name |
+ sizeof(MapIterator), // tp_basicsize |
+ 0, // tp_itemsize |
+ DeallocMapIterator, // tp_dealloc |
+ 0, // tp_print |
+ 0, // tp_getattr |
+ 0, // tp_setattr |
+ 0, // tp_compare |
+ 0, // tp_repr |
+ 0, // tp_as_number |
+ 0, // tp_as_sequence |
+ 0, // tp_as_mapping |
+ 0, // tp_hash |
+ 0, // tp_call |
+ 0, // tp_str |
+ 0, // tp_getattro |
+ 0, // tp_setattro |
+ 0, // tp_as_buffer |
+ Py_TPFLAGS_DEFAULT, // tp_flags |
+ "A scalar map iterator", // tp_doc |
+ 0, // tp_traverse |
+ 0, // tp_clear |
+ 0, // tp_richcompare |
+ 0, // tp_weaklistoffset |
+ PyObject_SelfIter, // tp_iter |
+ MapReflectionFriend::IterNext, // tp_iternext |
+ 0, // tp_methods |
+ 0, // tp_members |
+ 0, // tp_getset |
+ 0, // tp_base |
+ 0, // tp_dict |
+ 0, // tp_descr_get |
+ 0, // tp_descr_set |
+ 0, // tp_dictoffset |
+ 0, // tp_init |
+}; |
+ |
+} // namespace python |
+} // namespace protobuf |
+} // namespace google |