• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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: anuraag@google.com (Anuraag Agrawal)
32 // Author: tibell@google.com (Johan Tibell)
33 
34 #include <google/protobuf/pyext/repeated_composite_container.h>
35 
36 #include <memory>
37 #ifndef _SHARED_PTR_H
38 #include <google/protobuf/stubs/shared_ptr.h>
39 #endif
40 
41 #include <google/protobuf/stubs/common.h>
42 #include <google/protobuf/descriptor.h>
43 #include <google/protobuf/dynamic_message.h>
44 #include <google/protobuf/message.h>
45 #include <google/protobuf/pyext/descriptor.h>
46 #include <google/protobuf/pyext/message.h>
47 #include <google/protobuf/pyext/scoped_pyobject_ptr.h>
48 
49 #if PY_MAJOR_VERSION >= 3
50   #define PyInt_Check PyLong_Check
51   #define PyInt_AsLong PyLong_AsLong
52   #define PyInt_FromLong PyLong_FromLong
53 #endif
54 
55 namespace google {
56 namespace protobuf {
57 namespace python {
58 
59 extern google::protobuf::DynamicMessageFactory* global_message_factory;
60 
61 namespace repeated_composite_container {
62 
63 // TODO(tibell): We might also want to check:
64 //   GOOGLE_CHECK_NOTNULL((self)->owner.get());
65 #define GOOGLE_CHECK_ATTACHED(self)             \
66   do {                                   \
67     GOOGLE_CHECK_NOTNULL((self)->message);      \
68     GOOGLE_CHECK_NOTNULL((self)->parent_field); \
69   } while (0);
70 
71 #define GOOGLE_CHECK_RELEASED(self)             \
72   do {                                   \
73     GOOGLE_CHECK((self)->owner.get() == NULL);  \
74     GOOGLE_CHECK((self)->message == NULL);      \
75     GOOGLE_CHECK((self)->parent_field == NULL); \
76     GOOGLE_CHECK((self)->parent == NULL);       \
77   } while (0);
78 
79 // Returns a new reference.
GetKey(PyObject * x)80 static PyObject* GetKey(PyObject* x) {
81   // Just the identity function.
82   Py_INCREF(x);
83   return x;
84 }
85 
86 #define GET_KEY(keyfunc, value)                                         \
87   ((keyfunc) == NULL ?                                                  \
88   GetKey((value)) :                                                     \
89   PyObject_CallFunctionObjArgs((keyfunc), (value), NULL))
90 
91 // Converts a comparison function that returns -1, 0, or 1 into a
92 // less-than predicate.
93 //
94 // Returns -1 on error, 1 if x < y, 0 if x >= y.
islt(PyObject * x,PyObject * y,PyObject * compare)95 static int islt(PyObject *x, PyObject *y, PyObject *compare) {
96   if (compare == NULL)
97     return PyObject_RichCompareBool(x, y, Py_LT);
98 
99   ScopedPyObjectPtr res(PyObject_CallFunctionObjArgs(compare, x, y, NULL));
100   if (res == NULL)
101     return -1;
102   if (!PyInt_Check(res)) {
103     PyErr_Format(PyExc_TypeError,
104                  "comparison function must return int, not %.200s",
105                  Py_TYPE(res)->tp_name);
106     return -1;
107   }
108   return PyInt_AsLong(res) < 0;
109 }
110 
111 // Copied from uarrsort.c but swaps memcpy swaps with protobuf/python swaps
112 // TODO(anuraag): Is there a better way to do this then reinventing the wheel?
InternalQuickSort(RepeatedCompositeContainer * self,Py_ssize_t start,Py_ssize_t limit,PyObject * cmp,PyObject * keyfunc)113 static int InternalQuickSort(RepeatedCompositeContainer* self,
114                              Py_ssize_t start,
115                              Py_ssize_t limit,
116                              PyObject* cmp,
117                              PyObject* keyfunc) {
118   if (limit - start <= 1)
119     return 0;  // Nothing to sort.
120 
121   GOOGLE_CHECK_ATTACHED(self);
122 
123   google::protobuf::Message* message = self->message;
124   const google::protobuf::Reflection* reflection = message->GetReflection();
125   const google::protobuf::FieldDescriptor* descriptor = self->parent_field->descriptor;
126   Py_ssize_t left;
127   Py_ssize_t right;
128 
129   PyObject* children = self->child_messages;
130 
131   do {
132     left = start;
133     right = limit;
134     ScopedPyObjectPtr mid(
135         GET_KEY(keyfunc, PyList_GET_ITEM(children, (start + limit) / 2)));
136     do {
137       ScopedPyObjectPtr key(GET_KEY(keyfunc, PyList_GET_ITEM(children, left)));
138       int is_lt = islt(key, mid, cmp);
139       if (is_lt == -1)
140         return -1;
141       /* array[left]<x */
142       while (is_lt) {
143         ++left;
144         ScopedPyObjectPtr key(GET_KEY(keyfunc,
145                                       PyList_GET_ITEM(children, left)));
146         is_lt = islt(key, mid, cmp);
147         if (is_lt == -1)
148           return -1;
149       }
150       key.reset(GET_KEY(keyfunc, PyList_GET_ITEM(children, right - 1)));
151       is_lt = islt(mid, key, cmp);
152       if (is_lt == -1)
153         return -1;
154       while (is_lt) {
155         --right;
156         ScopedPyObjectPtr key(GET_KEY(keyfunc,
157                                       PyList_GET_ITEM(children, right - 1)));
158         is_lt = islt(mid, key, cmp);
159         if (is_lt == -1)
160           return -1;
161       }
162       if (left < right) {
163         --right;
164         if (left < right) {
165           reflection->SwapElements(message, descriptor, left, right);
166           PyObject* tmp = PyList_GET_ITEM(children, left);
167           PyList_SET_ITEM(children, left, PyList_GET_ITEM(children, right));
168           PyList_SET_ITEM(children, right, tmp);
169         }
170         ++left;
171       }
172     } while (left < right);
173 
174     if ((right - start) < (limit - left)) {
175       /* sort [start..right[ */
176       if (start < (right - 1)) {
177         InternalQuickSort(self, start, right, cmp, keyfunc);
178       }
179 
180       /* sort [left..limit[ */
181       start = left;
182     } else {
183       /* sort [left..limit[ */
184       if (left < (limit - 1)) {
185         InternalQuickSort(self, left, limit, cmp, keyfunc);
186       }
187 
188       /* sort [start..right[ */
189       limit = right;
190     }
191   } while (start < (limit - 1));
192 
193   return 0;
194 }
195 
196 #undef GET_KEY
197 
198 // ---------------------------------------------------------------------
199 // len()
200 
Length(RepeatedCompositeContainer * self)201 static Py_ssize_t Length(RepeatedCompositeContainer* self) {
202   google::protobuf::Message* message = self->message;
203   if (message != NULL) {
204     return message->GetReflection()->FieldSize(*message,
205                                                self->parent_field->descriptor);
206   } else {
207     // The container has been released (i.e. by a call to Clear() or
208     // ClearField() on the parent) and thus there's no message.
209     return PyList_GET_SIZE(self->child_messages);
210   }
211 }
212 
213 // Returns 0 if successful; returns -1 and sets an exception if
214 // unsuccessful.
UpdateChildMessages(RepeatedCompositeContainer * self)215 static int UpdateChildMessages(RepeatedCompositeContainer* self) {
216   if (self->message == NULL)
217     return 0;
218 
219   // A MergeFrom on a parent message could have caused extra messages to be
220   // added in the underlying protobuf so add them to our list. They can never
221   // be removed in such a way so there's no need to worry about that.
222   Py_ssize_t message_length = Length(self);
223   Py_ssize_t child_length = PyList_GET_SIZE(self->child_messages);
224   google::protobuf::Message* message = self->message;
225   const google::protobuf::Reflection* reflection = message->GetReflection();
226   for (Py_ssize_t i = child_length; i < message_length; ++i) {
227     const Message& sub_message = reflection->GetRepeatedMessage(
228         *(self->message), self->parent_field->descriptor, i);
229     ScopedPyObjectPtr py_cmsg(cmessage::NewEmpty(self->subclass_init));
230     if (py_cmsg == NULL) {
231       return -1;
232     }
233     CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg.get());
234     cmsg->owner = self->owner;
235     cmsg->message = const_cast<google::protobuf::Message*>(&sub_message);
236     cmsg->parent = self->parent;
237     if (cmessage::InitAttributes(cmsg, NULL, NULL) < 0) {
238       return -1;
239     }
240     PyList_Append(self->child_messages, py_cmsg);
241   }
242   return 0;
243 }
244 
245 // ---------------------------------------------------------------------
246 // add()
247 
AddToAttached(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)248 static PyObject* AddToAttached(RepeatedCompositeContainer* self,
249                                PyObject* args,
250                                PyObject* kwargs) {
251   GOOGLE_CHECK_ATTACHED(self);
252 
253   if (UpdateChildMessages(self) < 0) {
254     return NULL;
255   }
256   if (cmessage::AssureWritable(self->parent) == -1)
257     return NULL;
258   google::protobuf::Message* message = self->message;
259   google::protobuf::Message* sub_message =
260       message->GetReflection()->AddMessage(message,
261                                            self->parent_field->descriptor);
262   PyObject* py_cmsg = cmessage::NewEmpty(self->subclass_init);
263   if (py_cmsg == NULL) {
264     return NULL;
265   }
266   CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg);
267 
268   cmsg->owner = self->owner;
269   cmsg->message = sub_message;
270   cmsg->parent = self->parent;
271   // cmessage::InitAttributes must be called after cmsg->message has
272   // been set.
273   if (cmessage::InitAttributes(cmsg, NULL, kwargs) < 0) {
274     Py_DECREF(py_cmsg);
275     return NULL;
276   }
277   PyList_Append(self->child_messages, py_cmsg);
278   return py_cmsg;
279 }
280 
AddToReleased(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)281 static PyObject* AddToReleased(RepeatedCompositeContainer* self,
282                                PyObject* args,
283                                PyObject* kwargs) {
284   GOOGLE_CHECK_RELEASED(self);
285 
286   // Create the CMessage
287   PyObject* py_cmsg = PyObject_CallObject(self->subclass_init, NULL);
288   if (py_cmsg == NULL)
289     return NULL;
290   CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg);
291   if (cmessage::InitAttributes(cmsg, NULL, kwargs) < 0) {
292     Py_DECREF(py_cmsg);
293     return NULL;
294   }
295 
296   // The Message got created by the call to subclass_init above and
297   // it set self->owner to the newly allocated message.
298 
299   PyList_Append(self->child_messages, py_cmsg);
300   return py_cmsg;
301 }
302 
Add(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)303 PyObject* Add(RepeatedCompositeContainer* self,
304               PyObject* args,
305               PyObject* kwargs) {
306   if (self->message == NULL)
307     return AddToReleased(self, args, kwargs);
308   else
309     return AddToAttached(self, args, kwargs);
310 }
311 
312 // ---------------------------------------------------------------------
313 // extend()
314 
Extend(RepeatedCompositeContainer * self,PyObject * value)315 PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
316   cmessage::AssureWritable(self->parent);
317   if (UpdateChildMessages(self) < 0) {
318     return NULL;
319   }
320   ScopedPyObjectPtr iter(PyObject_GetIter(value));
321   if (iter == NULL) {
322     PyErr_SetString(PyExc_TypeError, "Value must be iterable");
323     return NULL;
324   }
325   ScopedPyObjectPtr next;
326   while ((next.reset(PyIter_Next(iter))) != NULL) {
327     if (!PyObject_TypeCheck(next, &CMessage_Type)) {
328       PyErr_SetString(PyExc_TypeError, "Not a cmessage");
329       return NULL;
330     }
331     ScopedPyObjectPtr new_message(Add(self, NULL, NULL));
332     if (new_message == NULL) {
333       return NULL;
334     }
335     CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
336     if (cmessage::MergeFrom(new_cmessage, next) == NULL) {
337       return NULL;
338     }
339   }
340   if (PyErr_Occurred()) {
341     return NULL;
342   }
343   Py_RETURN_NONE;
344 }
345 
MergeFrom(RepeatedCompositeContainer * self,PyObject * other)346 PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
347   if (UpdateChildMessages(self) < 0) {
348     return NULL;
349   }
350   return Extend(self, other);
351 }
352 
Subscript(RepeatedCompositeContainer * self,PyObject * slice)353 PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* slice) {
354   if (UpdateChildMessages(self) < 0) {
355     return NULL;
356   }
357   Py_ssize_t from;
358   Py_ssize_t to;
359   Py_ssize_t step;
360   Py_ssize_t length = Length(self);
361   Py_ssize_t slicelength;
362   if (PySlice_Check(slice)) {
363 #if PY_MAJOR_VERSION >= 3
364     if (PySlice_GetIndicesEx(slice,
365 #else
366     if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
367 #endif
368                              length, &from, &to, &step, &slicelength) == -1) {
369       return NULL;
370     }
371     return PyList_GetSlice(self->child_messages, from, to);
372   } else if (PyInt_Check(slice) || PyLong_Check(slice)) {
373     from = to = PyLong_AsLong(slice);
374     if (from < 0) {
375       from = to = length + from;
376     }
377     PyObject* result = PyList_GetItem(self->child_messages, from);
378     if (result == NULL) {
379       return NULL;
380     }
381     Py_INCREF(result);
382     return result;
383   }
384   PyErr_SetString(PyExc_TypeError, "index must be an integer or slice");
385   return NULL;
386 }
387 
AssignSubscript(RepeatedCompositeContainer * self,PyObject * slice,PyObject * value)388 int AssignSubscript(RepeatedCompositeContainer* self,
389                     PyObject* slice,
390                     PyObject* value) {
391   if (UpdateChildMessages(self) < 0) {
392     return -1;
393   }
394   if (value != NULL) {
395     PyErr_SetString(PyExc_TypeError, "does not support assignment");
396     return -1;
397   }
398 
399   // Delete from the underlying Message, if any.
400   if (self->message != NULL) {
401     if (cmessage::InternalDeleteRepeatedField(self->message,
402                                               self->parent_field->descriptor,
403                                               slice,
404                                               self->child_messages) < 0) {
405       return -1;
406     }
407   } else {
408     Py_ssize_t from;
409     Py_ssize_t to;
410     Py_ssize_t step;
411     Py_ssize_t length = Length(self);
412     Py_ssize_t slicelength;
413     if (PySlice_Check(slice)) {
414 #if PY_MAJOR_VERSION >= 3
415       if (PySlice_GetIndicesEx(slice,
416 #else
417       if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
418 #endif
419                                length, &from, &to, &step, &slicelength) == -1) {
420         return -1;
421       }
422       return PySequence_DelSlice(self->child_messages, from, to);
423     } else if (PyInt_Check(slice) || PyLong_Check(slice)) {
424       from = to = PyLong_AsLong(slice);
425       if (from < 0) {
426         from = to = length + from;
427       }
428       return PySequence_DelItem(self->child_messages, from);
429     }
430   }
431 
432   return 0;
433 }
434 
Remove(RepeatedCompositeContainer * self,PyObject * value)435 static PyObject* Remove(RepeatedCompositeContainer* self, PyObject* value) {
436   if (UpdateChildMessages(self) < 0) {
437     return NULL;
438   }
439   Py_ssize_t index = PySequence_Index(self->child_messages, value);
440   if (index == -1) {
441     return NULL;
442   }
443   ScopedPyObjectPtr py_index(PyLong_FromLong(index));
444   if (AssignSubscript(self, py_index, NULL) < 0) {
445     return NULL;
446   }
447   Py_RETURN_NONE;
448 }
449 
RichCompare(RepeatedCompositeContainer * self,PyObject * other,int opid)450 static PyObject* RichCompare(RepeatedCompositeContainer* self,
451                              PyObject* other,
452                              int opid) {
453   if (UpdateChildMessages(self) < 0) {
454     return NULL;
455   }
456   if (!PyObject_TypeCheck(other, &RepeatedCompositeContainer_Type)) {
457     PyErr_SetString(PyExc_TypeError,
458                     "Can only compare repeated composite fields "
459                     "against other repeated composite fields.");
460     return NULL;
461   }
462   if (opid == Py_EQ || opid == Py_NE) {
463     // TODO(anuraag): Don't make new lists just for this...
464     ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
465     if (full_slice == NULL) {
466       return NULL;
467     }
468     ScopedPyObjectPtr list(Subscript(self, full_slice));
469     if (list == NULL) {
470       return NULL;
471     }
472     ScopedPyObjectPtr other_list(
473         Subscript(
474             reinterpret_cast<RepeatedCompositeContainer*>(other), full_slice));
475     if (other_list == NULL) {
476       return NULL;
477     }
478     return PyObject_RichCompare(list, other_list, opid);
479   } else {
480     Py_INCREF(Py_NotImplemented);
481     return Py_NotImplemented;
482   }
483 }
484 
485 // ---------------------------------------------------------------------
486 // sort()
487 
SortAttached(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwds)488 static PyObject* SortAttached(RepeatedCompositeContainer* self,
489                               PyObject* args,
490                               PyObject* kwds) {
491   // Sort the underlying Message array.
492   PyObject *compare = NULL;
493   int reverse = 0;
494   PyObject *keyfunc = NULL;
495   static char *kwlist[] = {"cmp", "key", "reverse", 0};
496 
497   if (args != NULL) {
498     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:sort",
499                                      kwlist, &compare, &keyfunc, &reverse))
500       return NULL;
501   }
502   if (compare == Py_None)
503     compare = NULL;
504   if (keyfunc == Py_None)
505     keyfunc = NULL;
506 
507   const Py_ssize_t length = Length(self);
508   if (InternalQuickSort(self, 0, length, compare, keyfunc) < 0)
509     return NULL;
510 
511   // Finally reverse the result if requested.
512   if (reverse) {
513     google::protobuf::Message* message = self->message;
514     const google::protobuf::Reflection* reflection = message->GetReflection();
515     const google::protobuf::FieldDescriptor* descriptor = self->parent_field->descriptor;
516 
517     // Reverse the Message array.
518     for (int i = 0; i < length / 2; ++i)
519       reflection->SwapElements(message, descriptor, i, length - i - 1);
520 
521     // Reverse the Python list.
522     ScopedPyObjectPtr res(PyObject_CallMethod(self->child_messages,
523                                               "reverse", NULL));
524     if (res == NULL)
525       return NULL;
526   }
527 
528   Py_RETURN_NONE;
529 }
530 
SortReleased(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwds)531 static PyObject* SortReleased(RepeatedCompositeContainer* self,
532                               PyObject* args,
533                               PyObject* kwds) {
534   ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
535   if (m == NULL)
536     return NULL;
537   if (PyObject_Call(m, args, kwds) == NULL)
538     return NULL;
539   Py_RETURN_NONE;
540 }
541 
Sort(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwds)542 static PyObject* Sort(RepeatedCompositeContainer* self,
543                       PyObject* args,
544                       PyObject* kwds) {
545   // Support the old sort_function argument for backwards
546   // compatibility.
547   if (kwds != NULL) {
548     PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
549     if (sort_func != NULL) {
550       // Must set before deleting as sort_func is a borrowed reference
551       // and kwds might be the only thing keeping it alive.
552       PyDict_SetItemString(kwds, "cmp", sort_func);
553       PyDict_DelItemString(kwds, "sort_function");
554     }
555   }
556 
557   if (UpdateChildMessages(self) < 0)
558     return NULL;
559   if (self->message == NULL) {
560     return SortReleased(self, args, kwds);
561   } else {
562     return SortAttached(self, args, kwds);
563   }
564 }
565 
566 // ---------------------------------------------------------------------
567 
Item(RepeatedCompositeContainer * self,Py_ssize_t index)568 static PyObject* Item(RepeatedCompositeContainer* self, Py_ssize_t index) {
569   if (UpdateChildMessages(self) < 0) {
570     return NULL;
571   }
572   Py_ssize_t length = Length(self);
573   if (index < 0) {
574     index = length + index;
575   }
576   PyObject* item = PyList_GetItem(self->child_messages, index);
577   if (item == NULL) {
578     return NULL;
579   }
580   Py_INCREF(item);
581   return item;
582 }
583 
584 // The caller takes ownership of the returned Message.
ReleaseLast(const FieldDescriptor * field,const Descriptor * type,Message * message)585 Message* ReleaseLast(const FieldDescriptor* field,
586                      const Descriptor* type,
587                      Message* message) {
588   GOOGLE_CHECK_NOTNULL(field);
589   GOOGLE_CHECK_NOTNULL(type);
590   GOOGLE_CHECK_NOTNULL(message);
591 
592   Message* released_message = message->GetReflection()->ReleaseLast(
593       message, field);
594   // TODO(tibell): Deal with proto1.
595 
596   // ReleaseMessage will return NULL which differs from
597   // child_cmessage->message, if the field does not exist.  In this case,
598   // the latter points to the default instance via a const_cast<>, so we
599   // have to reset it to a new mutable object since we are taking ownership.
600   if (released_message == NULL) {
601     const Message* prototype = global_message_factory->GetPrototype(type);
602     GOOGLE_CHECK_NOTNULL(prototype);
603     return prototype->New();
604   } else {
605     return released_message;
606   }
607 }
608 
609 // Release field of message and transfer the ownership to cmessage.
ReleaseLastTo(const FieldDescriptor * field,Message * message,CMessage * cmessage)610 void ReleaseLastTo(const FieldDescriptor* field,
611                    Message* message,
612                    CMessage* cmessage) {
613   GOOGLE_CHECK_NOTNULL(field);
614   GOOGLE_CHECK_NOTNULL(message);
615   GOOGLE_CHECK_NOTNULL(cmessage);
616 
617   shared_ptr<Message> released_message(
618       ReleaseLast(field, cmessage->message->GetDescriptor(), message));
619   cmessage->parent = NULL;
620   cmessage->parent_field = NULL;
621   cmessage->message = released_message.get();
622   cmessage->read_only = false;
623   cmessage::SetOwner(cmessage, released_message);
624 }
625 
626 // Called to release a container using
627 // ClearField('container_field_name') on the parent.
Release(RepeatedCompositeContainer * self)628 int Release(RepeatedCompositeContainer* self) {
629   if (UpdateChildMessages(self) < 0) {
630     PyErr_WriteUnraisable(PyBytes_FromString("Failed to update released "
631                                              "messages"));
632     return -1;
633   }
634 
635   Message* message = self->message;
636   const FieldDescriptor* field = self->parent_field->descriptor;
637 
638   // The reflection API only lets us release the last message in a
639   // repeated field.  Therefore we iterate through the children
640   // starting with the last one.
641   const Py_ssize_t size = PyList_GET_SIZE(self->child_messages);
642   GOOGLE_DCHECK_EQ(size, message->GetReflection()->FieldSize(*message, field));
643   for (Py_ssize_t i = size - 1; i >= 0; --i) {
644     CMessage* child_cmessage = reinterpret_cast<CMessage*>(
645         PyList_GET_ITEM(self->child_messages, i));
646     ReleaseLastTo(field, message, child_cmessage);
647   }
648 
649   // Detach from containing message.
650   self->parent = NULL;
651   self->parent_field = NULL;
652   self->message = NULL;
653   self->owner.reset();
654 
655   return 0;
656 }
657 
SetOwner(RepeatedCompositeContainer * self,const shared_ptr<Message> & new_owner)658 int SetOwner(RepeatedCompositeContainer* self,
659              const shared_ptr<Message>& new_owner) {
660   GOOGLE_CHECK_ATTACHED(self);
661 
662   self->owner = new_owner;
663   const Py_ssize_t n = PyList_GET_SIZE(self->child_messages);
664   for (Py_ssize_t i = 0; i < n; ++i) {
665     PyObject* msg = PyList_GET_ITEM(self->child_messages, i);
666     if (cmessage::SetOwner(reinterpret_cast<CMessage*>(msg), new_owner) == -1) {
667       return -1;
668     }
669   }
670   return 0;
671 }
672 
Init(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)673 static int Init(RepeatedCompositeContainer* self,
674                 PyObject* args,
675                 PyObject* kwargs) {
676   self->message = NULL;
677   self->parent = NULL;
678   self->parent_field = NULL;
679   self->subclass_init = NULL;
680   self->child_messages = PyList_New(0);
681   return 0;
682 }
683 
Dealloc(RepeatedCompositeContainer * self)684 static void Dealloc(RepeatedCompositeContainer* self) {
685   Py_CLEAR(self->child_messages);
686   // TODO(tibell): Do we need to call delete on these objects to make
687   // sure their destructors are called?
688   self->owner.reset();
689   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
690 }
691 
692 static PySequenceMethods SqMethods = {
693   (lenfunc)Length,        /* sq_length */
694   0, /* sq_concat */
695   0, /* sq_repeat */
696   (ssizeargfunc)Item /* sq_item */
697 };
698 
699 static PyMappingMethods MpMethods = {
700   (lenfunc)Length,               /* mp_length */
701   (binaryfunc)Subscript,      /* mp_subscript */
702   (objobjargproc)AssignSubscript,/* mp_ass_subscript */
703 };
704 
705 static PyMethodDef Methods[] = {
706   { "add", (PyCFunction) Add, METH_VARARGS | METH_KEYWORDS,
707     "Adds an object to the repeated container." },
708   { "extend", (PyCFunction) Extend, METH_O,
709     "Adds objects to the repeated container." },
710   { "remove", (PyCFunction) Remove, METH_O,
711     "Removes an object from the repeated container." },
712   { "sort", (PyCFunction) Sort, METH_VARARGS | METH_KEYWORDS,
713     "Sorts the repeated container." },
714   { "MergeFrom", (PyCFunction) MergeFrom, METH_O,
715     "Adds objects to the repeated container." },
716   { NULL, NULL }
717 };
718 
719 }  // namespace repeated_composite_container
720 
721 PyTypeObject RepeatedCompositeContainer_Type = {
722   PyVarObject_HEAD_INIT(&PyType_Type, 0)
723   "google.protobuf.internal."
724   "cpp._message.RepeatedCompositeContainer",  // tp_name
725   sizeof(RepeatedCompositeContainer),     // tp_basicsize
726   0,                                   //  tp_itemsize
727   (destructor)repeated_composite_container::Dealloc,  //  tp_dealloc
728   0,                                   //  tp_print
729   0,                                   //  tp_getattr
730   0,                                   //  tp_setattr
731   0,                                   //  tp_compare
732   0,                                   //  tp_repr
733   0,                                   //  tp_as_number
734   &repeated_composite_container::SqMethods,   //  tp_as_sequence
735   &repeated_composite_container::MpMethods,   //  tp_as_mapping
736   0,                                   //  tp_hash
737   0,                                   //  tp_call
738   0,                                   //  tp_str
739   0,                                   //  tp_getattro
740   0,                                   //  tp_setattro
741   0,                                   //  tp_as_buffer
742   Py_TPFLAGS_DEFAULT,                  //  tp_flags
743   "A Repeated scalar container",       //  tp_doc
744   0,                                   //  tp_traverse
745   0,                                   //  tp_clear
746   (richcmpfunc)repeated_composite_container::RichCompare,  //  tp_richcompare
747   0,                                   //  tp_weaklistoffset
748   0,                                   //  tp_iter
749   0,                                   //  tp_iternext
750   repeated_composite_container::Methods,   //  tp_methods
751   0,                                   //  tp_members
752   0,                                   //  tp_getset
753   0,                                   //  tp_base
754   0,                                   //  tp_dict
755   0,                                   //  tp_descr_get
756   0,                                   //  tp_descr_set
757   0,                                   //  tp_dictoffset
758   (initproc)repeated_composite_container::Init,  //  tp_init
759 };
760 
761 }  // namespace python
762 }  // namespace protobuf
763 }  // namespace google
764