blob: 0fe98e73eff512f105d316de61a8243dce658874 [file] [log] [blame]
jieluo@google.combde4a322014-08-12 21:10:30 +00001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07003// https://developers.google.com/protocol-buffers/
jieluo@google.combde4a322014-08-12 21:10:30 +00004//
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
55namespace google {
56namespace protobuf {
57namespace python {
58
jieluo@google.combde4a322014-08-12 21:10:30 +000059namespace repeated_composite_container {
60
61// TODO(tibell): We might also want to check:
62// GOOGLE_CHECK_NOTNULL((self)->owner.get());
63#define GOOGLE_CHECK_ATTACHED(self) \
64 do { \
65 GOOGLE_CHECK_NOTNULL((self)->message); \
Feng Xiao6ef984a2014-11-10 17:34:54 -080066 GOOGLE_CHECK_NOTNULL((self)->parent_field_descriptor); \
jieluo@google.combde4a322014-08-12 21:10:30 +000067 } while (0);
68
69#define GOOGLE_CHECK_RELEASED(self) \
70 do { \
71 GOOGLE_CHECK((self)->owner.get() == NULL); \
72 GOOGLE_CHECK((self)->message == NULL); \
Feng Xiao6ef984a2014-11-10 17:34:54 -080073 GOOGLE_CHECK((self)->parent_field_descriptor == NULL); \
jieluo@google.combde4a322014-08-12 21:10:30 +000074 GOOGLE_CHECK((self)->parent == NULL); \
75 } while (0);
76
77// Returns a new reference.
78static PyObject* GetKey(PyObject* x) {
79 // Just the identity function.
80 Py_INCREF(x);
81 return x;
82}
83
84#define GET_KEY(keyfunc, value) \
85 ((keyfunc) == NULL ? \
86 GetKey((value)) : \
87 PyObject_CallFunctionObjArgs((keyfunc), (value), NULL))
88
89// Converts a comparison function that returns -1, 0, or 1 into a
90// less-than predicate.
91//
92// Returns -1 on error, 1 if x < y, 0 if x >= y.
93static int islt(PyObject *x, PyObject *y, PyObject *compare) {
94 if (compare == NULL)
95 return PyObject_RichCompareBool(x, y, Py_LT);
96
97 ScopedPyObjectPtr res(PyObject_CallFunctionObjArgs(compare, x, y, NULL));
98 if (res == NULL)
99 return -1;
100 if (!PyInt_Check(res)) {
101 PyErr_Format(PyExc_TypeError,
102 "comparison function must return int, not %.200s",
103 Py_TYPE(res)->tp_name);
104 return -1;
105 }
106 return PyInt_AsLong(res) < 0;
107}
108
109// Copied from uarrsort.c but swaps memcpy swaps with protobuf/python swaps
110// TODO(anuraag): Is there a better way to do this then reinventing the wheel?
111static int InternalQuickSort(RepeatedCompositeContainer* self,
112 Py_ssize_t start,
113 Py_ssize_t limit,
114 PyObject* cmp,
115 PyObject* keyfunc) {
116 if (limit - start <= 1)
117 return 0; // Nothing to sort.
118
119 GOOGLE_CHECK_ATTACHED(self);
120
Jisi Liuada65562015-02-25 16:39:11 -0800121 Message* message = self->message;
122 const Reflection* reflection = message->GetReflection();
123 const FieldDescriptor* descriptor = self->parent_field_descriptor;
jieluo@google.combde4a322014-08-12 21:10:30 +0000124 Py_ssize_t left;
125 Py_ssize_t right;
126
127 PyObject* children = self->child_messages;
128
129 do {
130 left = start;
131 right = limit;
132 ScopedPyObjectPtr mid(
133 GET_KEY(keyfunc, PyList_GET_ITEM(children, (start + limit) / 2)));
134 do {
135 ScopedPyObjectPtr key(GET_KEY(keyfunc, PyList_GET_ITEM(children, left)));
136 int is_lt = islt(key, mid, cmp);
137 if (is_lt == -1)
138 return -1;
139 /* array[left]<x */
140 while (is_lt) {
141 ++left;
142 ScopedPyObjectPtr key(GET_KEY(keyfunc,
143 PyList_GET_ITEM(children, left)));
144 is_lt = islt(key, mid, cmp);
145 if (is_lt == -1)
146 return -1;
147 }
148 key.reset(GET_KEY(keyfunc, PyList_GET_ITEM(children, right - 1)));
149 is_lt = islt(mid, key, cmp);
150 if (is_lt == -1)
151 return -1;
152 while (is_lt) {
153 --right;
154 ScopedPyObjectPtr key(GET_KEY(keyfunc,
155 PyList_GET_ITEM(children, right - 1)));
156 is_lt = islt(mid, key, cmp);
157 if (is_lt == -1)
158 return -1;
159 }
160 if (left < right) {
161 --right;
162 if (left < right) {
163 reflection->SwapElements(message, descriptor, left, right);
164 PyObject* tmp = PyList_GET_ITEM(children, left);
165 PyList_SET_ITEM(children, left, PyList_GET_ITEM(children, right));
166 PyList_SET_ITEM(children, right, tmp);
167 }
168 ++left;
169 }
170 } while (left < right);
171
172 if ((right - start) < (limit - left)) {
173 /* sort [start..right[ */
174 if (start < (right - 1)) {
175 InternalQuickSort(self, start, right, cmp, keyfunc);
176 }
177
178 /* sort [left..limit[ */
179 start = left;
180 } else {
181 /* sort [left..limit[ */
182 if (left < (limit - 1)) {
183 InternalQuickSort(self, left, limit, cmp, keyfunc);
184 }
185
186 /* sort [start..right[ */
187 limit = right;
188 }
189 } while (start < (limit - 1));
190
191 return 0;
192}
193
194#undef GET_KEY
195
196// ---------------------------------------------------------------------
197// len()
198
199static Py_ssize_t Length(RepeatedCompositeContainer* self) {
Jisi Liuada65562015-02-25 16:39:11 -0800200 Message* message = self->message;
jieluo@google.combde4a322014-08-12 21:10:30 +0000201 if (message != NULL) {
202 return message->GetReflection()->FieldSize(*message,
Feng Xiao6ef984a2014-11-10 17:34:54 -0800203 self->parent_field_descriptor);
jieluo@google.combde4a322014-08-12 21:10:30 +0000204 } else {
205 // The container has been released (i.e. by a call to Clear() or
206 // ClearField() on the parent) and thus there's no message.
207 return PyList_GET_SIZE(self->child_messages);
208 }
209}
210
211// Returns 0 if successful; returns -1 and sets an exception if
212// unsuccessful.
213static int UpdateChildMessages(RepeatedCompositeContainer* self) {
214 if (self->message == NULL)
215 return 0;
216
217 // A MergeFrom on a parent message could have caused extra messages to be
218 // added in the underlying protobuf so add them to our list. They can never
219 // be removed in such a way so there's no need to worry about that.
220 Py_ssize_t message_length = Length(self);
221 Py_ssize_t child_length = PyList_GET_SIZE(self->child_messages);
Jisi Liuada65562015-02-25 16:39:11 -0800222 Message* message = self->message;
223 const Reflection* reflection = message->GetReflection();
jieluo@google.combde4a322014-08-12 21:10:30 +0000224 for (Py_ssize_t i = child_length; i < message_length; ++i) {
225 const Message& sub_message = reflection->GetRepeatedMessage(
Feng Xiao6ef984a2014-11-10 17:34:54 -0800226 *(self->message), self->parent_field_descriptor, i);
227 CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
228 sub_message.GetDescriptor());
229 ScopedPyObjectPtr py_cmsg(reinterpret_cast<PyObject*>(cmsg));
230 if (cmsg == NULL) {
jieluo@google.combde4a322014-08-12 21:10:30 +0000231 return -1;
232 }
jieluo@google.combde4a322014-08-12 21:10:30 +0000233 cmsg->owner = self->owner;
Jisi Liuada65562015-02-25 16:39:11 -0800234 cmsg->message = const_cast<Message*>(&sub_message);
jieluo@google.combde4a322014-08-12 21:10:30 +0000235 cmsg->parent = self->parent;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800236 if (PyList_Append(self->child_messages, py_cmsg) < 0) {
jieluo@google.combde4a322014-08-12 21:10:30 +0000237 return -1;
238 }
jieluo@google.combde4a322014-08-12 21:10:30 +0000239 }
240 return 0;
241}
242
243// ---------------------------------------------------------------------
244// add()
245
246static PyObject* AddToAttached(RepeatedCompositeContainer* self,
247 PyObject* args,
248 PyObject* kwargs) {
249 GOOGLE_CHECK_ATTACHED(self);
250
251 if (UpdateChildMessages(self) < 0) {
252 return NULL;
253 }
254 if (cmessage::AssureWritable(self->parent) == -1)
255 return NULL;
Jisi Liuada65562015-02-25 16:39:11 -0800256 Message* message = self->message;
257 Message* sub_message =
jieluo@google.combde4a322014-08-12 21:10:30 +0000258 message->GetReflection()->AddMessage(message,
Feng Xiao6ef984a2014-11-10 17:34:54 -0800259 self->parent_field_descriptor);
260 CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
261 sub_message->GetDescriptor());
262 if (cmsg == NULL)
jieluo@google.combde4a322014-08-12 21:10:30 +0000263 return NULL;
jieluo@google.combde4a322014-08-12 21:10:30 +0000264
265 cmsg->owner = self->owner;
266 cmsg->message = sub_message;
267 cmsg->parent = self->parent;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800268 if (cmessage::InitAttributes(cmsg, kwargs) < 0) {
269 Py_DECREF(cmsg);
270 return NULL;
271 }
272
273 PyObject* py_cmsg = reinterpret_cast<PyObject*>(cmsg);
274 if (PyList_Append(self->child_messages, py_cmsg) < 0) {
jieluo@google.combde4a322014-08-12 21:10:30 +0000275 Py_DECREF(py_cmsg);
276 return NULL;
277 }
jieluo@google.combde4a322014-08-12 21:10:30 +0000278 return py_cmsg;
279}
280
281static PyObject* AddToReleased(RepeatedCompositeContainer* self,
282 PyObject* args,
283 PyObject* kwargs) {
284 GOOGLE_CHECK_RELEASED(self);
285
Feng Xiao6ef984a2014-11-10 17:34:54 -0800286 // Create a new Message detached from the rest.
287 PyObject* py_cmsg = PyEval_CallObjectWithKeywords(
288 self->subclass_init, NULL, kwargs);
jieluo@google.combde4a322014-08-12 21:10:30 +0000289 if (py_cmsg == NULL)
290 return NULL;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800291
292 if (PyList_Append(self->child_messages, py_cmsg) < 0) {
jieluo@google.combde4a322014-08-12 21:10:30 +0000293 Py_DECREF(py_cmsg);
294 return NULL;
295 }
jieluo@google.combde4a322014-08-12 21:10:30 +0000296 return py_cmsg;
297}
298
299PyObject* Add(RepeatedCompositeContainer* self,
300 PyObject* args,
301 PyObject* kwargs) {
302 if (self->message == NULL)
303 return AddToReleased(self, args, kwargs);
304 else
305 return AddToAttached(self, args, kwargs);
306}
307
308// ---------------------------------------------------------------------
309// extend()
310
311PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
312 cmessage::AssureWritable(self->parent);
313 if (UpdateChildMessages(self) < 0) {
314 return NULL;
315 }
316 ScopedPyObjectPtr iter(PyObject_GetIter(value));
317 if (iter == NULL) {
318 PyErr_SetString(PyExc_TypeError, "Value must be iterable");
319 return NULL;
320 }
321 ScopedPyObjectPtr next;
322 while ((next.reset(PyIter_Next(iter))) != NULL) {
323 if (!PyObject_TypeCheck(next, &CMessage_Type)) {
324 PyErr_SetString(PyExc_TypeError, "Not a cmessage");
325 return NULL;
326 }
327 ScopedPyObjectPtr new_message(Add(self, NULL, NULL));
328 if (new_message == NULL) {
329 return NULL;
330 }
331 CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
332 if (cmessage::MergeFrom(new_cmessage, next) == NULL) {
333 return NULL;
334 }
335 }
336 if (PyErr_Occurred()) {
337 return NULL;
338 }
339 Py_RETURN_NONE;
340}
341
342PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
343 if (UpdateChildMessages(self) < 0) {
344 return NULL;
345 }
346 return Extend(self, other);
347}
348
349PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* slice) {
350 if (UpdateChildMessages(self) < 0) {
351 return NULL;
352 }
Feng Xiao6ef984a2014-11-10 17:34:54 -0800353 // Just forward the call to the subscript-handling function of the
354 // list containing the child messages.
355 return PyObject_GetItem(self->child_messages, slice);
jieluo@google.combde4a322014-08-12 21:10:30 +0000356}
357
358int AssignSubscript(RepeatedCompositeContainer* self,
359 PyObject* slice,
360 PyObject* value) {
361 if (UpdateChildMessages(self) < 0) {
362 return -1;
363 }
364 if (value != NULL) {
365 PyErr_SetString(PyExc_TypeError, "does not support assignment");
366 return -1;
367 }
368
369 // Delete from the underlying Message, if any.
370 if (self->message != NULL) {
371 if (cmessage::InternalDeleteRepeatedField(self->message,
Feng Xiao6ef984a2014-11-10 17:34:54 -0800372 self->parent_field_descriptor,
jieluo@google.combde4a322014-08-12 21:10:30 +0000373 slice,
374 self->child_messages) < 0) {
375 return -1;
376 }
377 } else {
378 Py_ssize_t from;
379 Py_ssize_t to;
380 Py_ssize_t step;
381 Py_ssize_t length = Length(self);
382 Py_ssize_t slicelength;
383 if (PySlice_Check(slice)) {
384#if PY_MAJOR_VERSION >= 3
385 if (PySlice_GetIndicesEx(slice,
386#else
387 if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
388#endif
389 length, &from, &to, &step, &slicelength) == -1) {
390 return -1;
391 }
392 return PySequence_DelSlice(self->child_messages, from, to);
393 } else if (PyInt_Check(slice) || PyLong_Check(slice)) {
394 from = to = PyLong_AsLong(slice);
395 if (from < 0) {
396 from = to = length + from;
397 }
398 return PySequence_DelItem(self->child_messages, from);
399 }
400 }
401
402 return 0;
403}
404
405static PyObject* Remove(RepeatedCompositeContainer* self, PyObject* value) {
406 if (UpdateChildMessages(self) < 0) {
407 return NULL;
408 }
409 Py_ssize_t index = PySequence_Index(self->child_messages, value);
410 if (index == -1) {
411 return NULL;
412 }
413 ScopedPyObjectPtr py_index(PyLong_FromLong(index));
414 if (AssignSubscript(self, py_index, NULL) < 0) {
415 return NULL;
416 }
417 Py_RETURN_NONE;
418}
419
420static PyObject* RichCompare(RepeatedCompositeContainer* self,
421 PyObject* other,
422 int opid) {
423 if (UpdateChildMessages(self) < 0) {
424 return NULL;
425 }
426 if (!PyObject_TypeCheck(other, &RepeatedCompositeContainer_Type)) {
427 PyErr_SetString(PyExc_TypeError,
428 "Can only compare repeated composite fields "
429 "against other repeated composite fields.");
430 return NULL;
431 }
432 if (opid == Py_EQ || opid == Py_NE) {
433 // TODO(anuraag): Don't make new lists just for this...
434 ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
435 if (full_slice == NULL) {
436 return NULL;
437 }
438 ScopedPyObjectPtr list(Subscript(self, full_slice));
439 if (list == NULL) {
440 return NULL;
441 }
442 ScopedPyObjectPtr other_list(
443 Subscript(
444 reinterpret_cast<RepeatedCompositeContainer*>(other), full_slice));
445 if (other_list == NULL) {
446 return NULL;
447 }
448 return PyObject_RichCompare(list, other_list, opid);
449 } else {
450 Py_INCREF(Py_NotImplemented);
451 return Py_NotImplemented;
452 }
453}
454
455// ---------------------------------------------------------------------
456// sort()
457
458static PyObject* SortAttached(RepeatedCompositeContainer* self,
459 PyObject* args,
460 PyObject* kwds) {
461 // Sort the underlying Message array.
462 PyObject *compare = NULL;
463 int reverse = 0;
464 PyObject *keyfunc = NULL;
465 static char *kwlist[] = {"cmp", "key", "reverse", 0};
466
467 if (args != NULL) {
468 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:sort",
469 kwlist, &compare, &keyfunc, &reverse))
470 return NULL;
471 }
472 if (compare == Py_None)
473 compare = NULL;
474 if (keyfunc == Py_None)
475 keyfunc = NULL;
476
477 const Py_ssize_t length = Length(self);
478 if (InternalQuickSort(self, 0, length, compare, keyfunc) < 0)
479 return NULL;
480
481 // Finally reverse the result if requested.
482 if (reverse) {
Jisi Liuada65562015-02-25 16:39:11 -0800483 Message* message = self->message;
484 const Reflection* reflection = message->GetReflection();
485 const FieldDescriptor* descriptor = self->parent_field_descriptor;
jieluo@google.combde4a322014-08-12 21:10:30 +0000486
487 // Reverse the Message array.
488 for (int i = 0; i < length / 2; ++i)
489 reflection->SwapElements(message, descriptor, i, length - i - 1);
490
491 // Reverse the Python list.
492 ScopedPyObjectPtr res(PyObject_CallMethod(self->child_messages,
493 "reverse", NULL));
494 if (res == NULL)
495 return NULL;
496 }
497
498 Py_RETURN_NONE;
499}
500
501static PyObject* SortReleased(RepeatedCompositeContainer* self,
502 PyObject* args,
503 PyObject* kwds) {
504 ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
505 if (m == NULL)
506 return NULL;
507 if (PyObject_Call(m, args, kwds) == NULL)
508 return NULL;
509 Py_RETURN_NONE;
510}
511
512static PyObject* Sort(RepeatedCompositeContainer* self,
513 PyObject* args,
514 PyObject* kwds) {
515 // Support the old sort_function argument for backwards
516 // compatibility.
517 if (kwds != NULL) {
518 PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
519 if (sort_func != NULL) {
520 // Must set before deleting as sort_func is a borrowed reference
521 // and kwds might be the only thing keeping it alive.
522 PyDict_SetItemString(kwds, "cmp", sort_func);
523 PyDict_DelItemString(kwds, "sort_function");
524 }
525 }
526
Feng Xiao6ef984a2014-11-10 17:34:54 -0800527 if (UpdateChildMessages(self) < 0) {
jieluo@google.combde4a322014-08-12 21:10:30 +0000528 return NULL;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800529 }
jieluo@google.combde4a322014-08-12 21:10:30 +0000530 if (self->message == NULL) {
531 return SortReleased(self, args, kwds);
532 } else {
533 return SortAttached(self, args, kwds);
534 }
535}
536
537// ---------------------------------------------------------------------
538
539static PyObject* Item(RepeatedCompositeContainer* self, Py_ssize_t index) {
540 if (UpdateChildMessages(self) < 0) {
541 return NULL;
542 }
543 Py_ssize_t length = Length(self);
544 if (index < 0) {
545 index = length + index;
546 }
547 PyObject* item = PyList_GetItem(self->child_messages, index);
548 if (item == NULL) {
549 return NULL;
550 }
551 Py_INCREF(item);
552 return item;
553}
554
Jisi Liuada65562015-02-25 16:39:11 -0800555static PyObject* Pop(RepeatedCompositeContainer* self,
556 PyObject* args) {
557 Py_ssize_t index = -1;
558 if (!PyArg_ParseTuple(args, "|n", &index)) {
559 return NULL;
560 }
561 PyObject* item = Item(self, index);
562 if (item == NULL) {
563 PyErr_Format(PyExc_IndexError,
564 "list index (%zd) out of range",
565 index);
566 return NULL;
567 }
568 ScopedPyObjectPtr py_index(PyLong_FromSsize_t(index));
569 if (AssignSubscript(self, py_index, NULL) < 0) {
570 return NULL;
571 }
572 return item;
573}
574
jieluo@google.combde4a322014-08-12 21:10:30 +0000575// The caller takes ownership of the returned Message.
576Message* ReleaseLast(const FieldDescriptor* field,
577 const Descriptor* type,
578 Message* message) {
579 GOOGLE_CHECK_NOTNULL(field);
580 GOOGLE_CHECK_NOTNULL(type);
581 GOOGLE_CHECK_NOTNULL(message);
582
583 Message* released_message = message->GetReflection()->ReleaseLast(
584 message, field);
585 // TODO(tibell): Deal with proto1.
586
587 // ReleaseMessage will return NULL which differs from
588 // child_cmessage->message, if the field does not exist. In this case,
589 // the latter points to the default instance via a const_cast<>, so we
590 // have to reset it to a new mutable object since we are taking ownership.
591 if (released_message == NULL) {
Jisi Liuada65562015-02-25 16:39:11 -0800592 const Message* prototype =
593 cmessage::GetMessageFactory()->GetPrototype(type);
jieluo@google.combde4a322014-08-12 21:10:30 +0000594 GOOGLE_CHECK_NOTNULL(prototype);
595 return prototype->New();
596 } else {
597 return released_message;
598 }
599}
600
601// Release field of message and transfer the ownership to cmessage.
602void ReleaseLastTo(const FieldDescriptor* field,
603 Message* message,
604 CMessage* cmessage) {
605 GOOGLE_CHECK_NOTNULL(field);
606 GOOGLE_CHECK_NOTNULL(message);
607 GOOGLE_CHECK_NOTNULL(cmessage);
608
609 shared_ptr<Message> released_message(
610 ReleaseLast(field, cmessage->message->GetDescriptor(), message));
611 cmessage->parent = NULL;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800612 cmessage->parent_field_descriptor = NULL;
jieluo@google.combde4a322014-08-12 21:10:30 +0000613 cmessage->message = released_message.get();
614 cmessage->read_only = false;
615 cmessage::SetOwner(cmessage, released_message);
616}
617
618// Called to release a container using
619// ClearField('container_field_name') on the parent.
620int Release(RepeatedCompositeContainer* self) {
621 if (UpdateChildMessages(self) < 0) {
622 PyErr_WriteUnraisable(PyBytes_FromString("Failed to update released "
623 "messages"));
624 return -1;
625 }
626
627 Message* message = self->message;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800628 const FieldDescriptor* field = self->parent_field_descriptor;
jieluo@google.combde4a322014-08-12 21:10:30 +0000629
630 // The reflection API only lets us release the last message in a
631 // repeated field. Therefore we iterate through the children
632 // starting with the last one.
633 const Py_ssize_t size = PyList_GET_SIZE(self->child_messages);
634 GOOGLE_DCHECK_EQ(size, message->GetReflection()->FieldSize(*message, field));
635 for (Py_ssize_t i = size - 1; i >= 0; --i) {
636 CMessage* child_cmessage = reinterpret_cast<CMessage*>(
637 PyList_GET_ITEM(self->child_messages, i));
638 ReleaseLastTo(field, message, child_cmessage);
639 }
640
641 // Detach from containing message.
642 self->parent = NULL;
Feng Xiao6ef984a2014-11-10 17:34:54 -0800643 self->parent_field_descriptor = NULL;
jieluo@google.combde4a322014-08-12 21:10:30 +0000644 self->message = NULL;
645 self->owner.reset();
646
647 return 0;
648}
649
650int SetOwner(RepeatedCompositeContainer* self,
651 const shared_ptr<Message>& new_owner) {
652 GOOGLE_CHECK_ATTACHED(self);
653
654 self->owner = new_owner;
655 const Py_ssize_t n = PyList_GET_SIZE(self->child_messages);
656 for (Py_ssize_t i = 0; i < n; ++i) {
657 PyObject* msg = PyList_GET_ITEM(self->child_messages, i);
658 if (cmessage::SetOwner(reinterpret_cast<CMessage*>(msg), new_owner) == -1) {
659 return -1;
660 }
661 }
662 return 0;
663}
664
Feng Xiao6ef984a2014-11-10 17:34:54 -0800665// The private constructor of RepeatedCompositeContainer objects.
666PyObject *NewContainer(
667 CMessage* parent,
Jisi Liuada65562015-02-25 16:39:11 -0800668 const FieldDescriptor* parent_field_descriptor,
Feng Xiao6ef984a2014-11-10 17:34:54 -0800669 PyObject *concrete_class) {
670 if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
671 return NULL;
672 }
673
674 RepeatedCompositeContainer* self =
675 reinterpret_cast<RepeatedCompositeContainer*>(
676 PyType_GenericAlloc(&RepeatedCompositeContainer_Type, 0));
677 if (self == NULL) {
678 return NULL;
679 }
680
681 self->message = parent->message;
682 self->parent = parent;
683 self->parent_field_descriptor = parent_field_descriptor;
684 self->owner = parent->owner;
685 Py_INCREF(concrete_class);
686 self->subclass_init = concrete_class;
jieluo@google.combde4a322014-08-12 21:10:30 +0000687 self->child_messages = PyList_New(0);
Feng Xiao6ef984a2014-11-10 17:34:54 -0800688
689 return reinterpret_cast<PyObject*>(self);
jieluo@google.combde4a322014-08-12 21:10:30 +0000690}
691
692static void Dealloc(RepeatedCompositeContainer* self) {
693 Py_CLEAR(self->child_messages);
Feng Xiao6ef984a2014-11-10 17:34:54 -0800694 Py_CLEAR(self->subclass_init);
jieluo@google.combde4a322014-08-12 21:10:30 +0000695 // TODO(tibell): Do we need to call delete on these objects to make
696 // sure their destructors are called?
697 self->owner.reset();
Feng Xiao6ef984a2014-11-10 17:34:54 -0800698
jieluo@google.combde4a322014-08-12 21:10:30 +0000699 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
700}
701
702static PySequenceMethods SqMethods = {
703 (lenfunc)Length, /* sq_length */
704 0, /* sq_concat */
705 0, /* sq_repeat */
706 (ssizeargfunc)Item /* sq_item */
707};
708
709static PyMappingMethods MpMethods = {
710 (lenfunc)Length, /* mp_length */
711 (binaryfunc)Subscript, /* mp_subscript */
712 (objobjargproc)AssignSubscript,/* mp_ass_subscript */
713};
714
715static PyMethodDef Methods[] = {
716 { "add", (PyCFunction) Add, METH_VARARGS | METH_KEYWORDS,
717 "Adds an object to the repeated container." },
718 { "extend", (PyCFunction) Extend, METH_O,
719 "Adds objects to the repeated container." },
Jisi Liuada65562015-02-25 16:39:11 -0800720 { "pop", (PyCFunction)Pop, METH_VARARGS,
721 "Removes an object from the repeated container and returns it." },
jieluo@google.combde4a322014-08-12 21:10:30 +0000722 { "remove", (PyCFunction) Remove, METH_O,
723 "Removes an object from the repeated container." },
724 { "sort", (PyCFunction) Sort, METH_VARARGS | METH_KEYWORDS,
725 "Sorts the repeated container." },
726 { "MergeFrom", (PyCFunction) MergeFrom, METH_O,
727 "Adds objects to the repeated container." },
728 { NULL, NULL }
729};
730
731} // namespace repeated_composite_container
732
733PyTypeObject RepeatedCompositeContainer_Type = {
734 PyVarObject_HEAD_INIT(&PyType_Type, 0)
Jisi Liu885b6122015-02-28 14:51:22 -0800735 // Keep the fully qualified _message symbol in a line for opensource.
736 "google.protobuf.pyext._message."
737 "RepeatedCompositeContainer", // tp_name
738 sizeof(RepeatedCompositeContainer), // tp_basicsize
jieluo@google.combde4a322014-08-12 21:10:30 +0000739 0, // tp_itemsize
740 (destructor)repeated_composite_container::Dealloc, // tp_dealloc
741 0, // tp_print
742 0, // tp_getattr
743 0, // tp_setattr
744 0, // tp_compare
745 0, // tp_repr
746 0, // tp_as_number
747 &repeated_composite_container::SqMethods, // tp_as_sequence
748 &repeated_composite_container::MpMethods, // tp_as_mapping
749 0, // tp_hash
750 0, // tp_call
751 0, // tp_str
752 0, // tp_getattro
753 0, // tp_setattro
754 0, // tp_as_buffer
755 Py_TPFLAGS_DEFAULT, // tp_flags
756 "A Repeated scalar container", // tp_doc
757 0, // tp_traverse
758 0, // tp_clear
759 (richcmpfunc)repeated_composite_container::RichCompare, // tp_richcompare
760 0, // tp_weaklistoffset
761 0, // tp_iter
762 0, // tp_iternext
763 repeated_composite_container::Methods, // tp_methods
764 0, // tp_members
765 0, // tp_getset
766 0, // tp_base
767 0, // tp_dict
768 0, // tp_descr_get
769 0, // tp_descr_set
770 0, // tp_dictoffset
Feng Xiao6ef984a2014-11-10 17:34:54 -0800771 0, // tp_init
jieluo@google.combde4a322014-08-12 21:10:30 +0000772};
773
774} // namespace python
775} // namespace protobuf
776} // namespace google