|
|
|
/*
|
|
|
|
* Copyright (c) 2009-2021, Google LLC
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* 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 LLC 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 Google LLC 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "python/repeated.h"
|
|
|
|
|
|
|
|
#include "python/convert.h"
|
|
|
|
#include "python/message.h"
|
|
|
|
#include "python/protobuf.h"
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedCompositeContainer_Append(PyObject* _self,
|
|
|
|
PyObject* value);
|
|
|
|
static PyObject* PyUpb_RepeatedScalarContainer_Append(PyObject* _self,
|
|
|
|
PyObject* value);
|
|
|
|
|
|
|
|
// For an expression like:
|
|
|
|
// foo[index]
|
|
|
|
//
|
|
|
|
// Converts `index` to an effective i/count/step, for a repeated field
|
|
|
|
// field of size `size`.
|
|
|
|
static bool IndexToRange(PyObject* index, Py_ssize_t size, Py_ssize_t* i,
|
|
|
|
Py_ssize_t* count, Py_ssize_t* step) {
|
|
|
|
assert(i && count && step);
|
|
|
|
if (PySlice_Check(index)) {
|
|
|
|
Py_ssize_t start, stop;
|
|
|
|
if (PySlice_Unpack(index, &start, &stop, step) < 0) return false;
|
|
|
|
*count = PySlice_AdjustIndices(size, &start, &stop, *step);
|
|
|
|
*i = start;
|
|
|
|
} else {
|
|
|
|
*i = PyNumber_AsSsize_t(index, PyExc_IndexError);
|
|
|
|
|
|
|
|
if (*i == -1 && PyErr_Occurred()) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "list indices must be integers");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*i < 0) *i += size;
|
|
|
|
*step = 0;
|
|
|
|
*count = 1;
|
|
|
|
|
|
|
|
if (*i < 0 || size <= *i) {
|
|
|
|
PyErr_Format(PyExc_IndexError, "list index out of range");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wrapper for a repeated field.
|
|
|
|
typedef struct {
|
|
|
|
PyObject_HEAD
|
|
|
|
PyObject* arena;
|
|
|
|
// The field descriptor (PyObject*).
|
|
|
|
// The low bit indicates whether the container is reified (see ptr below).
|
|
|
|
// - low bit set: repeated field is a stub (no underlying data).
|
|
|
|
// - low bit clear: repeated field is reified (points to upb_array).
|
|
|
|
uintptr_t field;
|
|
|
|
union {
|
|
|
|
PyObject* parent; // stub: owning pointer to parent message.
|
|
|
|
upb_array* arr; // reified: the data for this array.
|
|
|
|
} ptr;
|
|
|
|
} PyUpb_RepeatedContainer;
|
|
|
|
|
|
|
|
static bool PyUpb_RepeatedContainer_IsStub(PyUpb_RepeatedContainer* self) {
|
|
|
|
return self->field & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_GetFieldDescriptor(
|
|
|
|
PyUpb_RepeatedContainer* self) {
|
|
|
|
return (PyObject*)(self->field & ~(uintptr_t)1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const upb_fielddef* PyUpb_RepeatedContainer_GetField(
|
|
|
|
PyUpb_RepeatedContainer* self) {
|
|
|
|
return PyUpb_FieldDescriptor_GetDef(
|
|
|
|
PyUpb_RepeatedContainer_GetFieldDescriptor(self));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the repeated field is reified, returns it. Otherwise, returns NULL.
|
|
|
|
// If NULL is returned, the object is empty and has no underlying data.
|
|
|
|
static upb_array* PyUpb_RepeatedContainer_GetIfReified(
|
|
|
|
PyUpb_RepeatedContainer* self) {
|
|
|
|
return PyUpb_RepeatedContainer_IsStub(self) ? NULL : self->ptr.arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PyUpb_RepeatedContainer_Reify(PyObject* _self, upb_array* arr) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
assert(PyUpb_RepeatedContainer_IsStub(self));
|
|
|
|
PyUpb_ObjCache_Add(arr, &self->ob_base);
|
|
|
|
Py_DECREF(self->ptr.parent);
|
|
|
|
self->ptr.arr = arr; // Overwrites self->ptr.parent.
|
|
|
|
self->field &= ~(uintptr_t)1;
|
|
|
|
assert(!PyUpb_RepeatedContainer_IsStub(self));
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_array* PyUpb_RepeatedContainer_EnsureReified(PyObject* _self) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
if (arr) return arr; // Already writable.
|
|
|
|
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
arr = upb_array_new(arena, upb_fielddef_type(f));
|
|
|
|
PyUpb_CMessage_SetConcreteSubobj(self->ptr.parent, f,
|
|
|
|
(upb_msgval){.array_val = arr});
|
|
|
|
PyUpb_RepeatedContainer_Reify((PyObject*)self, arr);
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PyUpb_RepeatedContainer_Dealloc(PyObject* _self) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
Py_DECREF(self->arena);
|
|
|
|
if (PyUpb_RepeatedContainer_IsStub(self)) {
|
|
|
|
PyUpb_CMessage_CacheDelete(self->ptr.parent,
|
|
|
|
PyUpb_RepeatedContainer_GetField(self));
|
|
|
|
Py_DECREF(self->ptr.parent);
|
|
|
|
} else {
|
|
|
|
PyUpb_ObjCache_Delete(self->ptr.arr);
|
|
|
|
}
|
|
|
|
Py_DECREF(PyUpb_RepeatedContainer_GetFieldDescriptor(self));
|
|
|
|
PyUpb_Dealloc(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyTypeObject* PyUpb_RepeatedContainer_GetClass(const upb_fielddef* f) {
|
|
|
|
assert(upb_fielddef_isseq(f) && !upb_fielddef_ismap(f));
|
|
|
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
|
|
|
|
return upb_fielddef_issubmsg(f) ? state->repeated_composite_container_type
|
|
|
|
: state->repeated_scalar_container_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Py_ssize_t PyUpb_RepeatedContainer_Length(PyObject* self) {
|
|
|
|
upb_array* arr =
|
|
|
|
PyUpb_RepeatedContainer_GetIfReified((PyUpb_RepeatedContainer*)self);
|
|
|
|
return arr ? upb_array_size(arr) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedContainer_NewStub(PyObject* parent,
|
|
|
|
const upb_fielddef* f,
|
|
|
|
PyObject* arena) {
|
|
|
|
PyTypeObject* cls = PyUpb_RepeatedContainer_GetClass(f);
|
|
|
|
PyUpb_RepeatedContainer* repeated = (void*)PyType_GenericAlloc(cls, 0);
|
|
|
|
repeated->arena = arena;
|
|
|
|
repeated->field = (uintptr_t)PyUpb_FieldDescriptor_Get(f) | 1;
|
|
|
|
repeated->ptr.parent = parent;
|
|
|
|
Py_INCREF(arena);
|
|
|
|
Py_INCREF(parent);
|
|
|
|
return &repeated->ob_base;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedContainer_GetOrCreateWrapper(upb_array* arr,
|
|
|
|
const upb_fielddef* f,
|
|
|
|
PyObject* arena) {
|
|
|
|
PyObject* ret = PyUpb_ObjCache_Get(arr);
|
|
|
|
if (ret) return ret;
|
|
|
|
|
|
|
|
PyTypeObject* cls = PyUpb_RepeatedContainer_GetClass(f);
|
|
|
|
PyUpb_RepeatedContainer* repeated = (void*)PyType_GenericAlloc(cls, 0);
|
|
|
|
repeated->arena = arena;
|
|
|
|
repeated->field = (uintptr_t)PyUpb_FieldDescriptor_Get(f);
|
|
|
|
repeated->ptr.arr = arr;
|
|
|
|
ret = &repeated->ob_base;
|
|
|
|
Py_INCREF(arena);
|
|
|
|
PyUpb_ObjCache_Add(arr, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_MergeFrom(PyObject* _self,
|
|
|
|
PyObject* args);
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedContainer_DeepCopy(PyObject* _self, PyObject* value) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
PyUpb_RepeatedContainer* clone = (void*)PyType_GenericAlloc(Py_TYPE(_self), 0);
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
if (clone == NULL) return NULL;
|
|
|
|
clone->arena = PyUpb_Arena_New();
|
|
|
|
clone->field = (uintptr_t)PyUpb_FieldDescriptor_Get(f);
|
|
|
|
clone->ptr.arr =
|
|
|
|
upb_array_new(PyUpb_Arena_Get(clone->arena), upb_fielddef_type(f));
|
|
|
|
PyUpb_ObjCache_Add(clone->ptr.arr, (PyObject*)clone);
|
|
|
|
if (!PyUpb_RepeatedContainer_MergeFrom((PyObject*)clone, _self)) {
|
|
|
|
Py_DECREF(clone);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return (PyObject*)clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedContainer_Extend(PyObject* _self, PyObject* value) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
size_t start_size = upb_array_size(arr);
|
|
|
|
PyObject* it = PyObject_GetIter(value);
|
|
|
|
if (!it) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "Value must be iterable");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
bool submsg = upb_fielddef_issubmsg(f);
|
|
|
|
PyObject* e;
|
|
|
|
|
|
|
|
while ((e = PyIter_Next(it))) {
|
|
|
|
PyObject* ret;
|
|
|
|
if (submsg) {
|
|
|
|
ret = PyUpb_RepeatedCompositeContainer_Append(_self, e);
|
|
|
|
} else {
|
|
|
|
ret = PyUpb_RepeatedScalarContainer_Append(_self, e);
|
|
|
|
}
|
|
|
|
Py_XDECREF(ret);
|
|
|
|
Py_DECREF(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_DECREF(it);
|
|
|
|
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
upb_array_resize(arr, start_size, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Item(PyObject* _self,
|
|
|
|
Py_ssize_t index) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
Py_ssize_t size = arr ? upb_array_size(arr) : 0;
|
|
|
|
if (index < 0 || index >= size) {
|
|
|
|
PyErr_Format(PyExc_IndexError, "list index (%zd) out of range", index);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
return PyUpb_UpbToPy(upb_array_get(arr, index), f, self->arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedContainer_ToList(PyObject* _self) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
if (!arr) return PyList_New(0);
|
|
|
|
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
size_t n = upb_array_size(arr);
|
|
|
|
PyObject* list = PyList_New(n);
|
|
|
|
for (size_t i = 0; i < n; i++) {
|
|
|
|
PyObject* val = PyUpb_UpbToPy(upb_array_get(arr, i), f, self->arena);
|
|
|
|
if (!val) {
|
|
|
|
Py_DECREF(list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyList_SetItem(list, i, val);
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Repr(PyObject* _self) {
|
|
|
|
PyObject* list = PyUpb_RepeatedContainer_ToList(_self);
|
|
|
|
if (!list) return NULL;
|
|
|
|
assert(!PyErr_Occurred());
|
|
|
|
PyObject* repr = PyObject_Repr(list);
|
|
|
|
Py_DECREF(list);
|
|
|
|
return repr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_RichCompare(PyObject* _self,
|
|
|
|
PyObject* _other,
|
|
|
|
int opid) {
|
|
|
|
if (opid != Py_EQ && opid != Py_NE) {
|
|
|
|
Py_INCREF(Py_NotImplemented);
|
|
|
|
return Py_NotImplemented;
|
|
|
|
}
|
|
|
|
PyObject* list1 = PyUpb_RepeatedContainer_ToList(_self);
|
|
|
|
PyObject* list2 = _other;
|
|
|
|
PyObject* del = NULL;
|
|
|
|
if (PyObject_TypeCheck(_other, _self->ob_type)) {
|
|
|
|
del = list2 = PyUpb_RepeatedContainer_ToList(_other);
|
|
|
|
}
|
|
|
|
PyObject* ret = PyObject_RichCompare(list1, list2, opid);
|
|
|
|
Py_DECREF(list1);
|
|
|
|
Py_XDECREF(del);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Subscript(PyObject* _self,
|
|
|
|
PyObject* key) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
Py_ssize_t size = arr ? upb_array_size(arr) : 0;
|
|
|
|
Py_ssize_t idx, count, step;
|
|
|
|
if (!IndexToRange(key, size, &idx, &count, &step)) return NULL;
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
if (step == 0) {
|
|
|
|
return PyUpb_UpbToPy(upb_array_get(arr, idx), f, self->arena);
|
|
|
|
} else {
|
|
|
|
PyObject* list = PyList_New(count);
|
|
|
|
for (Py_ssize_t i = 0; i < count; i++, idx += step) {
|
|
|
|
upb_msgval msgval = upb_array_get(self->ptr.arr, idx);
|
|
|
|
PyObject* item = PyUpb_UpbToPy(msgval, f, self->arena);
|
|
|
|
if (!item) {
|
|
|
|
Py_DECREF(list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PyList_SetItem(list, i, item);
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PyUpb_RepeatedContainer_SetSubscript(
|
|
|
|
PyUpb_RepeatedContainer* self, upb_array* arr, const upb_fielddef* f,
|
|
|
|
Py_ssize_t idx, Py_ssize_t count, Py_ssize_t step, PyObject* value) {
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
if (upb_fielddef_issubmsg(f)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "does not support assignment");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (step == 0) {
|
|
|
|
// Set single value.
|
|
|
|
upb_msgval msgval;
|
|
|
|
if (!PyUpb_PyToUpb(value, f, &msgval, arena)) return -1;
|
|
|
|
upb_array_set(arr, idx, msgval);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set range.
|
|
|
|
PyObject* seq =
|
|
|
|
PySequence_Fast(value, "must assign iterable to extended slice");
|
|
|
|
PyObject* item = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
if (!seq) goto err;
|
|
|
|
if (PySequence_Size(seq) != count) {
|
|
|
|
PyErr_Format(PyExc_ValueError,
|
|
|
|
"attempt to assign sequence of size %zd to extended slice "
|
|
|
|
"of size %zd",
|
|
|
|
PySequence_Size(seq), count);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
for (Py_ssize_t i = 0; i < count; i++, idx += step) {
|
|
|
|
upb_msgval msgval;
|
|
|
|
item = PySequence_GetItem(seq, i);
|
|
|
|
if (!item) goto err;
|
|
|
|
// XXX: if this fails we can leave the list partially mutated.
|
|
|
|
if (!PyUpb_PyToUpb(item, f, &msgval, arena)) goto err;
|
|
|
|
Py_DECREF(item);
|
|
|
|
item = NULL;
|
|
|
|
upb_array_set(arr, idx, msgval);
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
Py_XDECREF(seq);
|
|
|
|
Py_XDECREF(item);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PyUpb_RepeatedContainer_DeleteSubscript(upb_array* arr,
|
|
|
|
Py_ssize_t idx,
|
|
|
|
Py_ssize_t count,
|
|
|
|
Py_ssize_t step) {
|
|
|
|
// Normalize direction: deletion is order-independent.
|
|
|
|
Py_ssize_t start = idx;
|
|
|
|
if (step < 0) {
|
|
|
|
Py_ssize_t end = start + step * (count - 1);
|
|
|
|
start = end;
|
|
|
|
step = -step;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t dst = start;
|
|
|
|
size_t src;
|
|
|
|
if (step > 1) {
|
|
|
|
// Move elements between steps:
|
|
|
|
//
|
|
|
|
// src
|
|
|
|
// |
|
|
|
|
// |------X---X---X---X------------------------------|
|
|
|
|
// |
|
|
|
|
// dst <-------- tail -------------->
|
|
|
|
src = start + 1;
|
|
|
|
for (Py_ssize_t i = 1; i < count; i++, dst += step - 1, src += step) {
|
|
|
|
upb_array_move(arr, dst, src, step);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
src = start + count;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move tail.
|
|
|
|
size_t tail = upb_array_size(arr) - src;
|
|
|
|
size_t new_size = dst + tail;
|
|
|
|
assert(new_size == upb_array_size(arr) - count);
|
|
|
|
upb_array_move(arr, dst, src, tail);
|
|
|
|
upb_array_resize(arr, new_size, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PyUpb_RepeatedContainer_AssignSubscript(PyObject* _self,
|
|
|
|
PyObject* key,
|
|
|
|
PyObject* value) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
Py_ssize_t size = arr ? upb_array_size(arr) : 0;
|
|
|
|
Py_ssize_t idx, count, step;
|
|
|
|
if (!IndexToRange(key, size, &idx, &count, &step)) return -1;
|
|
|
|
if (value) {
|
|
|
|
return PyUpb_RepeatedContainer_SetSubscript(self, arr, f, idx, count, step,
|
|
|
|
value);
|
|
|
|
} else {
|
|
|
|
return PyUpb_RepeatedContainer_DeleteSubscript(arr, idx, count, step);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Pop(PyObject* _self, PyObject* args) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
Py_ssize_t index = -1;
|
|
|
|
if (!PyArg_ParseTuple(args, "|n", &index)) return NULL;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
size_t size = upb_array_size(arr);
|
|
|
|
if (index < 0) index += size;
|
|
|
|
if (index >= size) index = size - 1;
|
|
|
|
PyObject* ret = PyUpb_RepeatedContainer_Item(_self, index);
|
|
|
|
if (!ret) return NULL;
|
|
|
|
upb_array_delete(self->ptr.arr, index, 1);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Remove(PyObject* _self,
|
|
|
|
PyObject* value) {
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
Py_ssize_t match_index = -1;
|
|
|
|
Py_ssize_t n = PyUpb_RepeatedContainer_Length(_self);
|
|
|
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
|
|
|
PyObject* elem = PyUpb_RepeatedContainer_Item(_self, i);
|
|
|
|
if (!elem) return NULL;
|
|
|
|
int eq = PyObject_RichCompareBool(elem, value, Py_EQ);
|
|
|
|
Py_DECREF(elem);
|
|
|
|
if (eq) {
|
|
|
|
match_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (match_index == -1) {
|
|
|
|
PyErr_SetString(PyExc_ValueError, "remove(x): x not in container");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (PyUpb_RepeatedContainer_DeleteSubscript(arr, match_index, 1, 1) < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A helper function used only for Sort().
|
|
|
|
static bool PyUpb_RepeatedContainer_Assign(PyObject* _self, PyObject* list) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
Py_ssize_t size = PyList_Size(list);
|
|
|
|
bool submsg = upb_fielddef_issubmsg(f);
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
for (Py_ssize_t i = 0; i < size; ++i) {
|
|
|
|
PyObject* obj = PyList_GetItem(list, i);
|
|
|
|
upb_msgval msgval;
|
|
|
|
if (submsg) {
|
|
|
|
msgval.msg_val = PyUpb_CMessage_GetIfReified(obj);
|
|
|
|
assert(msgval.msg_val);
|
|
|
|
} else {
|
|
|
|
if (!PyUpb_PyToUpb(obj, f, &msgval, arena)) return false;
|
|
|
|
}
|
|
|
|
upb_array_set(arr, i, msgval);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Sort(PyObject* pself, PyObject* args,
|
|
|
|
PyObject* kwds) {
|
|
|
|
// Support the old sort_function argument for backwards
|
|
|
|
// compatibility.
|
|
|
|
if (kwds != NULL) {
|
|
|
|
PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
|
|
|
|
if (sort_func != NULL) {
|
|
|
|
// Must set before deleting as sort_func is a borrowed reference
|
|
|
|
// and kwds might be the only thing keeping it alive.
|
|
|
|
if (PyDict_SetItemString(kwds, "cmp", sort_func) == -1) return NULL;
|
|
|
|
if (PyDict_DelItemString(kwds, "sort_function") == -1) return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* ret = NULL;
|
|
|
|
PyObject* full_slice = NULL;
|
|
|
|
PyObject* list = NULL;
|
|
|
|
PyObject* m = NULL;
|
|
|
|
PyObject* res = NULL;
|
|
|
|
if ((full_slice = PySlice_New(NULL, NULL, NULL)) &&
|
|
|
|
(list = PyUpb_RepeatedContainer_Subscript(pself, full_slice)) &&
|
|
|
|
(m = PyObject_GetAttrString(list, "sort")) &&
|
|
|
|
(res = PyObject_Call(m, args, kwds)) &&
|
|
|
|
PyUpb_RepeatedContainer_Assign(pself, list)) {
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
ret = Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Py_XDECREF(full_slice);
|
|
|
|
Py_XDECREF(list);
|
|
|
|
Py_XDECREF(m);
|
|
|
|
Py_XDECREF(res);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_MergeFrom(PyObject* _self,
|
|
|
|
PyObject* args) {
|
|
|
|
return PyUpb_RepeatedContainer_Extend(_self, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// RepeatedCompositeContainer
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedCompositeContainer_AppendNew(PyObject* _self) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
if (!arr) return NULL;
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
const upb_msgdef* m = upb_fielddef_msgsubdef(f);
|
|
|
|
upb_msg* msg = upb_msg_new(m, arena);
|
|
|
|
upb_msgval msgval = {.msg_val = msg};
|
|
|
|
upb_array_append(arr, msgval, arena);
|
|
|
|
return PyUpb_CMessage_Get(msg, m, self->arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_RepeatedCompositeContainer_Add(PyObject* _self, PyObject* args,
|
|
|
|
PyObject* kwargs) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
PyObject* py_msg = PyUpb_RepeatedCompositeContainer_AppendNew(_self);
|
|
|
|
if (!py_msg) return NULL;
|
|
|
|
if (PyUpb_CMessage_InitAttributes(py_msg, args, kwargs) < 0) {
|
|
|
|
Py_DECREF(py_msg);
|
|
|
|
upb_array_delete(self->ptr.arr, upb_array_size(self->ptr.arr) - 1, 1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return py_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedCompositeContainer_Append(PyObject* _self,
|
|
|
|
PyObject* value) {
|
|
|
|
if (!PyUpb_CMessage_Check(value)) return NULL;
|
|
|
|
PyObject* py_msg = PyUpb_RepeatedCompositeContainer_AppendNew(_self);
|
|
|
|
if (!py_msg) return NULL;
|
|
|
|
PyObject* none = PyUpb_CMessage_MergeFrom(py_msg, value);
|
|
|
|
if (!none) {
|
|
|
|
Py_DECREF(py_msg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Py_DECREF(none);
|
|
|
|
return py_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedContainer_Insert(PyObject* _self,
|
|
|
|
PyObject* args) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
Py_ssize_t index;
|
|
|
|
PyObject* value;
|
|
|
|
if (!PyArg_ParseTuple(args, "nO", &index, &value)) return NULL;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
if (!arr) return NULL;
|
|
|
|
|
|
|
|
// Normalize index.
|
|
|
|
Py_ssize_t size = upb_array_size(arr);
|
|
|
|
if (index < 0) index += size;
|
|
|
|
if (index < 0) index = 0;
|
|
|
|
if (index > size) index = size;
|
|
|
|
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_msgval msgval;
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
if (upb_fielddef_issubmsg(f)) {
|
|
|
|
// Create message.
|
|
|
|
const upb_msgdef* m = upb_fielddef_msgsubdef(f);
|
|
|
|
upb_msg* msg = upb_msg_new(m, arena);
|
|
|
|
PyObject* py_msg = PyUpb_CMessage_Get(msg, m, self->arena);
|
|
|
|
PyObject* ret = PyUpb_CMessage_MergeFrom(py_msg, value);
|
|
|
|
Py_DECREF(py_msg);
|
|
|
|
if (!ret) return NULL;
|
|
|
|
Py_DECREF(ret);
|
|
|
|
msgval.msg_val = msg;
|
|
|
|
} else {
|
|
|
|
if (!PyUpb_PyToUpb(value, f, &msgval, arena)) return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_array_insert(arr, index, 1, arena);
|
|
|
|
upb_array_set(arr, index, msgval);
|
|
|
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef PyUpb_RepeatedCompositeContainer_Methods[] = {
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
{"__deepcopy__", PyUpb_RepeatedContainer_DeepCopy, METH_VARARGS,
|
|
|
|
"Makes a deep copy of the class."},
|
|
|
|
{"add", (PyCFunction)PyUpb_RepeatedCompositeContainer_Add,
|
|
|
|
METH_VARARGS | METH_KEYWORDS, "Adds an object to the repeated container."},
|
|
|
|
{"append", PyUpb_RepeatedCompositeContainer_Append, METH_O,
|
|
|
|
"Appends a message to the end of the repeated container."},
|
|
|
|
{"insert", PyUpb_RepeatedContainer_Insert, METH_VARARGS,
|
|
|
|
"Inserts a message before the specified index."},
|
|
|
|
{"extend", PyUpb_RepeatedContainer_Extend, METH_O,
|
|
|
|
"Adds objects to the repeated container."},
|
|
|
|
{"pop", PyUpb_RepeatedContainer_Pop, METH_VARARGS,
|
|
|
|
"Removes an object from the repeated container and returns it."},
|
|
|
|
{"remove", PyUpb_RepeatedContainer_Remove, METH_O,
|
|
|
|
"Removes an object from the repeated container."},
|
|
|
|
{"sort", (PyCFunction)PyUpb_RepeatedContainer_Sort,
|
|
|
|
METH_VARARGS | METH_KEYWORDS, "Sorts the repeated container."},
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
//{"reverse", reinterpret_cast<PyCFunction>(Reverse), METH_NOARGS,
|
|
|
|
// "Reverses elements order of the repeated container."},
|
|
|
|
{"MergeFrom", PyUpb_RepeatedContainer_MergeFrom, METH_O,
|
|
|
|
"Adds objects to the repeated container."},
|
|
|
|
{NULL, NULL}};
|
|
|
|
|
|
|
|
static PyType_Slot PyUpb_RepeatedCompositeContainer_Slots[] = {
|
|
|
|
{Py_tp_dealloc, PyUpb_RepeatedContainer_Dealloc},
|
|
|
|
{Py_tp_methods, PyUpb_RepeatedCompositeContainer_Methods},
|
|
|
|
{Py_sq_length, PyUpb_RepeatedContainer_Length},
|
|
|
|
{Py_sq_item, PyUpb_RepeatedContainer_Item},
|
|
|
|
{Py_mp_length, PyUpb_RepeatedContainer_Length},
|
|
|
|
{Py_tp_repr, PyUpb_RepeatedContainer_Repr},
|
|
|
|
{Py_mp_subscript, PyUpb_RepeatedContainer_Subscript},
|
|
|
|
{Py_mp_ass_subscript, PyUpb_RepeatedContainer_AssignSubscript},
|
|
|
|
{Py_tp_new, PyUpb_Forbidden_New},
|
|
|
|
{Py_tp_hash, PyObject_HashNotImplemented},
|
|
|
|
{0, NULL}};
|
|
|
|
|
|
|
|
static PyType_Spec PyUpb_RepeatedCompositeContainer_Spec = {
|
|
|
|
PYUPB_MODULE_NAME ".RepeatedCompositeContainer",
|
|
|
|
sizeof(PyUpb_RepeatedContainer),
|
|
|
|
0, // tp_itemsize
|
|
|
|
Py_TPFLAGS_DEFAULT,
|
|
|
|
PyUpb_RepeatedCompositeContainer_Slots,
|
|
|
|
};
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// RepeatedScalarContainer
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static PyObject* PyUpb_RepeatedScalarContainer_Append(PyObject* _self,
|
|
|
|
PyObject* value) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_EnsureReified(_self);
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_msgval msgval;
|
|
|
|
if (!PyUpb_PyToUpb(value, f, &msgval, arena)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
upb_array_append(arr, msgval, arena);
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int PyUpb_RepeatedScalarContainer_AssignItem(PyObject* _self,
|
|
|
|
Py_ssize_t index,
|
|
|
|
PyObject* item) {
|
|
|
|
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;
|
|
|
|
upb_array* arr = PyUpb_RepeatedContainer_GetIfReified(self);
|
|
|
|
Py_ssize_t size = arr ? upb_array_size(arr) : 0;
|
|
|
|
if (index < 0 || index >= size) {
|
|
|
|
PyErr_Format(PyExc_IndexError, "list index (%zd) out of range", index);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const upb_fielddef* f = PyUpb_RepeatedContainer_GetField(self);
|
|
|
|
upb_msgval msgval;
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
if (!PyUpb_PyToUpb(item, f, &msgval, arena)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
upb_array_set(self->ptr.arr, index, msgval);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef PyUpb_RepeatedScalarContainer_Methods[] = {
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
{"__deepcopy__", PyUpb_RepeatedContainer_DeepCopy, METH_VARARGS,
|
|
|
|
"Makes a deep copy of the class."},
|
|
|
|
// {"__reduce__", Reduce, METH_NOARGS,
|
|
|
|
// "Outputs picklable representation of the repeated field."},
|
|
|
|
{"append", PyUpb_RepeatedScalarContainer_Append, METH_O,
|
|
|
|
"Appends an object to the repeated container."},
|
|
|
|
{"extend", PyUpb_RepeatedContainer_Extend, METH_O,
|
|
|
|
"Appends objects to the repeated container."},
|
|
|
|
{"insert", PyUpb_RepeatedContainer_Insert, METH_VARARGS,
|
|
|
|
"Inserts an object at the specified position in the container."},
|
|
|
|
{"pop", PyUpb_RepeatedContainer_Pop, METH_VARARGS,
|
|
|
|
"Removes an object from the repeated container and returns it."},
|
|
|
|
{"remove", PyUpb_RepeatedContainer_Remove, METH_O,
|
|
|
|
"Removes an object from the repeated container."},
|
|
|
|
{"sort", (PyCFunction)PyUpb_RepeatedContainer_Sort,
|
|
|
|
METH_VARARGS | METH_KEYWORDS, "Sorts the repeated container."},
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
// {"reverse", reinterpret_cast<PyCFunction>(Reverse), METH_NOARGS,
|
|
|
|
// "Reverses elements order of the repeated container."},
|
|
|
|
{"MergeFrom", PyUpb_RepeatedContainer_MergeFrom, METH_O,
|
|
|
|
"Merges a repeated container into the current container."},
|
|
|
|
{NULL, NULL}};
|
|
|
|
|
|
|
|
static PyType_Slot PyUpb_RepeatedScalarContainer_Slots[] = {
|
|
|
|
{Py_tp_dealloc, PyUpb_RepeatedContainer_Dealloc},
|
|
|
|
{Py_tp_methods, PyUpb_RepeatedScalarContainer_Methods},
|
|
|
|
{Py_tp_new, PyUpb_Forbidden_New},
|
|
|
|
{Py_tp_repr, PyUpb_RepeatedContainer_Repr},
|
|
|
|
{Py_sq_length, PyUpb_RepeatedContainer_Length},
|
|
|
|
{Py_sq_item, PyUpb_RepeatedContainer_Item},
|
|
|
|
{Py_sq_ass_item, PyUpb_RepeatedScalarContainer_AssignItem},
|
|
|
|
{Py_mp_length, PyUpb_RepeatedContainer_Length},
|
|
|
|
{Py_mp_subscript, PyUpb_RepeatedContainer_Subscript},
|
|
|
|
{Py_mp_ass_subscript, PyUpb_RepeatedContainer_AssignSubscript},
|
|
|
|
{Py_tp_richcompare, PyUpb_RepeatedContainer_RichCompare},
|
|
|
|
{Py_tp_hash, PyObject_HashNotImplemented},
|
|
|
|
{0, NULL}};
|
|
|
|
|
|
|
|
static PyType_Spec PyUpb_RepeatedScalarContainer_Spec = {
|
|
|
|
PYUPB_MODULE_NAME ".RepeatedScalarContainer",
|
|
|
|
sizeof(PyUpb_RepeatedContainer),
|
|
|
|
0, // tp_itemsize
|
|
|
|
Py_TPFLAGS_DEFAULT,
|
|
|
|
PyUpb_RepeatedScalarContainer_Slots,
|
|
|
|
};
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Top Level
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static bool PyUpb_Repeated_RegisterAsSequence(PyUpb_ModuleState* state) {
|
|
|
|
PyObject* collections = NULL;
|
|
|
|
PyObject* seq = NULL;
|
|
|
|
PyObject* ret1 = NULL;
|
|
|
|
PyObject* ret2 = NULL;
|
|
|
|
PyTypeObject* type1 = state->repeated_scalar_container_type;
|
|
|
|
PyTypeObject* type2 = state->repeated_composite_container_type;
|
|
|
|
|
|
|
|
bool ok = (collections = PyImport_ImportModule("collections.abc")) &&
|
|
|
|
(seq = PyObject_GetAttrString(collections, "MutableSequence")) &&
|
|
|
|
(ret1 = PyObject_CallMethod(seq, "register", "O", type1)) &&
|
|
|
|
(ret2 = PyObject_CallMethod(seq, "register", "O", type2));
|
|
|
|
|
|
|
|
Py_XDECREF(collections);
|
|
|
|
Py_XDECREF(seq);
|
|
|
|
Py_XDECREF(ret1);
|
|
|
|
Py_XDECREF(ret2);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PyUpb_Repeated_Init(PyObject* m) {
|
|
|
|
PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m);
|
|
|
|
|
|
|
|
state->repeated_composite_container_type =
|
|
|
|
PyUpb_AddClass(m, &PyUpb_RepeatedCompositeContainer_Spec);
|
|
|
|
state->repeated_scalar_container_type =
|
|
|
|
PyUpb_AddClass(m, &PyUpb_RepeatedScalarContainer_Spec);
|
|
|
|
|
|
|
|
return state->repeated_composite_container_type &&
|
|
|
|
state->repeated_scalar_container_type &&
|
|
|
|
PyUpb_Repeated_RegisterAsSequence(state);
|
|
|
|
}
|