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