parent
10597019ba
commit
bf74b3e7fd
9 changed files with 575 additions and 55 deletions
@ -0,0 +1,487 @@ |
|||||||
|
/*
|
||||||
|
* 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/map.h" |
||||||
|
|
||||||
|
#include "python/convert.h" |
||||||
|
#include "python/message.h" |
||||||
|
#include "python/protobuf.h" |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// MapContainer
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
PyObject_HEAD |
||||||
|
PyObject* arena; |
||||||
|
uintptr_t field; // upb_fielddef*, low bit 1 == unset
|
||||||
|
union { |
||||||
|
upb_map* map; // when set, the data for this array.
|
||||||
|
PyObject* parent; // when unset owning pointer to parent message.
|
||||||
|
}; |
||||||
|
int version; |
||||||
|
} PyUpb_MapContainer; |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapIterator_New(PyUpb_MapContainer* map); |
||||||
|
|
||||||
|
static bool PyUpb_MapContainer_IsUnset(PyUpb_MapContainer* self) { |
||||||
|
return self->field & 1; |
||||||
|
} |
||||||
|
|
||||||
|
static upb_map* PyUpb_MapContainer_GetIfWritable(PyUpb_MapContainer* self) { |
||||||
|
return PyUpb_MapContainer_IsUnset(self) ? NULL : self->map; |
||||||
|
} |
||||||
|
|
||||||
|
static const upb_fielddef* PyUpb_MapContainer_GetField( |
||||||
|
PyUpb_MapContainer* self) { |
||||||
|
return (const upb_fielddef*)(self->field & ~(uintptr_t)1); |
||||||
|
} |
||||||
|
|
||||||
|
static void PyUpb_MapContainer_Dealloc(void* _self) { |
||||||
|
PyUpb_MapContainer* self = _self; |
||||||
|
Py_DECREF(self->arena); |
||||||
|
if (PyUpb_MapContainer_IsUnset(self)) { |
||||||
|
PyUpb_CMessage_CacheDelete(self->parent, PyUpb_MapContainer_GetField(self)); |
||||||
|
Py_DECREF(self->parent); |
||||||
|
} else { |
||||||
|
PyUpb_ObjCache_Delete(self->map); |
||||||
|
} |
||||||
|
PyUpb_Dealloc(_self); |
||||||
|
} |
||||||
|
|
||||||
|
PyTypeObject* PyUpb_MapContainer_GetClass(const upb_fielddef* f) { |
||||||
|
assert(upb_fielddef_ismap(f)); |
||||||
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
||||||
|
return upb_fielddef_issubmsg(f) ? state->message_map_container_type |
||||||
|
: state->scalar_map_container_type; |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_NewUnset(PyObject* parent, const upb_fielddef* f, |
||||||
|
PyObject* arena) { |
||||||
|
PyTypeObject* cls = PyUpb_MapContainer_GetClass(f); |
||||||
|
// We are GC because of the MutableMapping base class. Ideally we could
|
||||||
|
// implement them ourselves so we don't need a base so we don't need to be GC.
|
||||||
|
PyUpb_MapContainer* map = (void*)PyType_GenericAlloc(cls, 0); |
||||||
|
map->arena = arena; |
||||||
|
map->field = (uintptr_t)f | 1; |
||||||
|
map->parent = parent; |
||||||
|
map->version = 0; |
||||||
|
Py_INCREF(arena); |
||||||
|
Py_INCREF(parent); |
||||||
|
return &map->ob_base; |
||||||
|
} |
||||||
|
|
||||||
|
void PyUpb_MapContainer_SwitchToSet(PyObject* _self, upb_map* map) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
PyUpb_ObjCache_Add(map, &self->ob_base); |
||||||
|
Py_DECREF(self->parent); |
||||||
|
self->map = map; // Overwrites self->parent.
|
||||||
|
self->field = self->field & ~(uintptr_t)1; |
||||||
|
assert(!PyUpb_MapContainer_IsUnset(self)); |
||||||
|
} |
||||||
|
|
||||||
|
void PyUpb_MapContainer_Invalidate(PyObject* obj) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)obj; |
||||||
|
self->version++; |
||||||
|
} |
||||||
|
|
||||||
|
static upb_map* PyUpb_MapContainer_AssureWritable(PyUpb_MapContainer* self) { |
||||||
|
self->version++; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
if (map) return map; // Already writable.
|
||||||
|
|
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
upb_arena* arena = PyUpb_Arena_Get(self->arena); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1); |
||||||
|
map = upb_map_new(arena, upb_fielddef_type(key_f), upb_fielddef_type(val_f)); |
||||||
|
upb_msgval msgval = {.map_val = map}; |
||||||
|
PyUpb_CMessage_SetConcreteSubobj(self->parent, f, msgval); |
||||||
|
PyUpb_MapContainer_SwitchToSet((PyObject*)self, map); |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
int PyUpb_MapContainer_AssignSubscript(PyObject* _self, PyObject* key, |
||||||
|
PyObject* val) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_AssureWritable(self); |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1); |
||||||
|
upb_arena* arena = PyUpb_Arena_Get(self->arena); |
||||||
|
upb_msgval u_key, u_val; |
||||||
|
if (!PyUpb_PyToUpb(key, key_f, &u_key, arena)) return -1; |
||||||
|
|
||||||
|
if (val) { |
||||||
|
if (!PyUpb_PyToUpb(val, val_f, &u_val, arena)) return -1; |
||||||
|
upb_map_set(map, u_key, u_val, arena); |
||||||
|
} else { |
||||||
|
if (!upb_map_delete(map, u_key)) { |
||||||
|
PyErr_Format(PyExc_KeyError, "Key not present in map"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_Contains(PyObject* _self, PyObject* key) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
if (!map) Py_RETURN_FALSE; |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
upb_msgval u_key; |
||||||
|
if (!PyUpb_PyToUpb(key, key_f, &u_key, NULL)) return NULL; |
||||||
|
if (upb_map_get(map, u_key, NULL)) { |
||||||
|
Py_RETURN_TRUE; |
||||||
|
} else { |
||||||
|
Py_RETURN_FALSE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_Clear(PyObject* _self, PyObject* key) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
if (map) upb_map_clear(map); |
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapContainer_Get(PyObject* _self, PyObject* args, |
||||||
|
PyObject* kwargs) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
static const char* kwlist[] = {"key", "default", NULL}; |
||||||
|
PyObject* key; |
||||||
|
PyObject* default_value = NULL; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", (char**)kwlist, &key, |
||||||
|
&default_value)) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1); |
||||||
|
upb_arena* arena = PyUpb_Arena_Get(self->arena); |
||||||
|
upb_msgval u_key, u_val; |
||||||
|
if (!PyUpb_PyToUpb(key, key_f, &u_key, arena)) return NULL; |
||||||
|
if (map && upb_map_get(map, u_key, &u_val)) { |
||||||
|
return PyUpb_UpbToPy(u_val, val_f, self->arena); |
||||||
|
} else { |
||||||
|
if (default_value) { |
||||||
|
Py_INCREF(default_value); |
||||||
|
return default_value; |
||||||
|
} else { |
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapContainer_GetEntryClass(PyObject* _self, |
||||||
|
PyObject* arg) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
return PyUpb_Descriptor_GetClass(entry_m); |
||||||
|
} |
||||||
|
|
||||||
|
Py_ssize_t PyUpb_MapContainer_Length(PyObject* _self) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
return map ? upb_map_size(map) : 0; |
||||||
|
} |
||||||
|
|
||||||
|
PyUpb_MapContainer* PyUpb_MapContainer_Check(PyObject* _self) { |
||||||
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
||||||
|
if (!PyObject_TypeCheck(_self, state->message_map_container_type) && |
||||||
|
!PyObject_TypeCheck(_self, state->scalar_map_container_type)) { |
||||||
|
PyErr_Format(PyExc_TypeError, "Expected protobuf map, but got %R", _self); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
return (PyUpb_MapContainer*)_self; |
||||||
|
} |
||||||
|
|
||||||
|
int PyUpb_CMessage_InitMapAttributes(PyObject* map, PyObject* value, |
||||||
|
const upb_fielddef* f); |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapContainer_MergeFrom(PyObject* _self, PyObject* _arg) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
|
||||||
|
if (PyDict_Check(_arg)) { |
||||||
|
return PyErr_Format(PyExc_AttributeError, "Merging of dict is not allowed"); |
||||||
|
} |
||||||
|
|
||||||
|
if (PyUpb_CMessage_InitMapAttributes(_self, _arg, f) < 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapContainer_Repr(PyObject* _self) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
PyObject* dict = PyDict_New(); |
||||||
|
if (map) { |
||||||
|
size_t iter = UPB_MAP_BEGIN; |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1); |
||||||
|
while (upb_mapiter_next(map, &iter)) { |
||||||
|
PyObject* key = |
||||||
|
PyUpb_UpbToPy(upb_mapiter_key(map, iter), key_f, self->arena); |
||||||
|
PyObject* val = |
||||||
|
PyUpb_UpbToPy(upb_mapiter_value(map, iter), val_f, self->arena); |
||||||
|
PyDict_SetItem(dict, key, val); |
||||||
|
Py_DECREF(key); |
||||||
|
Py_DECREF(val); |
||||||
|
} |
||||||
|
} |
||||||
|
PyObject* repr = PyObject_Repr(dict); |
||||||
|
Py_DECREF(dict); |
||||||
|
return repr; |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_Subscript(PyObject* _self, PyObject* key) { |
||||||
|
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self; |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self); |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1); |
||||||
|
upb_arena* arena = PyUpb_Arena_Get(self->arena); |
||||||
|
upb_msgval u_key, u_val; |
||||||
|
if (!PyUpb_PyToUpb(key, key_f, &u_key, arena)) return NULL; |
||||||
|
if (!map || !upb_map_get(map, u_key, &u_val)) { |
||||||
|
map = PyUpb_MapContainer_AssureWritable(self); |
||||||
|
upb_arena* arena = PyUpb_Arena_Get(self->arena); |
||||||
|
if (upb_fielddef_issubmsg(val_f)) { |
||||||
|
u_val.msg_val = upb_msg_new(upb_fielddef_msgsubdef(val_f), arena); |
||||||
|
} else { |
||||||
|
memset(&u_val, 0, sizeof(u_val)); |
||||||
|
} |
||||||
|
upb_map_set(map, u_key, u_val, arena); |
||||||
|
} |
||||||
|
return PyUpb_UpbToPy(u_val, val_f, self->arena); |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_GetOrCreateWrapper(upb_map* u_map, |
||||||
|
const upb_fielddef* f, |
||||||
|
PyObject* arena) { |
||||||
|
PyObject* ret = PyUpb_ObjCache_Get(u_map); |
||||||
|
|
||||||
|
if (!ret) { |
||||||
|
PyTypeObject* cls = PyUpb_MapContainer_GetClass(f); |
||||||
|
PyUpb_MapContainer* map = (void*)PyType_GenericAlloc(cls, 0); |
||||||
|
map->arena = arena; |
||||||
|
map->field = (uintptr_t)f; |
||||||
|
map->map = u_map; |
||||||
|
map->version = 0; |
||||||
|
ret = &map->ob_base; |
||||||
|
Py_INCREF(arena); |
||||||
|
PyUpb_ObjCache_Add(u_map, ret); |
||||||
|
} |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// ScalarMapContainer
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static PyMethodDef PyUpb_ScalarMapContainer_Methods[] = { |
||||||
|
{"__contains__", PyUpb_MapContainer_Contains, METH_O, |
||||||
|
"Tests whether a key is a member of the map."}, |
||||||
|
{"clear", PyUpb_MapContainer_Clear, METH_NOARGS, |
||||||
|
"Removes all elements from the map."}, |
||||||
|
{"get", (PyCFunction)PyUpb_MapContainer_Get, METH_VARARGS | METH_KEYWORDS, |
||||||
|
"Gets the value for the given key if present, or otherwise a default"}, |
||||||
|
{"GetEntryClass", PyUpb_MapContainer_GetEntryClass, METH_NOARGS, |
||||||
|
"Return the class used to build Entries of (key, value) pairs."}, |
||||||
|
{"MergeFrom", PyUpb_MapContainer_MergeFrom, METH_O, |
||||||
|
"Merges a map into the current map."}, |
||||||
|
/*
|
||||||
|
{ "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, |
||||||
|
"Makes a deep copy of the class." }, |
||||||
|
{ "__reduce__", (PyCFunction)Reduce, METH_NOARGS, |
||||||
|
"Outputs picklable representation of the repeated field." }, |
||||||
|
*/ |
||||||
|
{NULL, NULL}, |
||||||
|
}; |
||||||
|
|
||||||
|
static PyType_Slot PyUpb_ScalarMapContainer_Slots[] = { |
||||||
|
{Py_tp_dealloc, PyUpb_MapContainer_Dealloc}, |
||||||
|
{Py_mp_length, PyUpb_MapContainer_Length}, |
||||||
|
{Py_mp_subscript, PyUpb_MapContainer_Subscript}, |
||||||
|
{Py_mp_ass_subscript, PyUpb_MapContainer_AssignSubscript}, |
||||||
|
{Py_tp_methods, PyUpb_ScalarMapContainer_Methods}, |
||||||
|
{Py_tp_iter, PyUpb_MapIterator_New}, |
||||||
|
{Py_tp_repr, PyUpb_MapContainer_Repr}, |
||||||
|
{Py_tp_hash, PyObject_HashNotImplemented}, |
||||||
|
{0, NULL}, |
||||||
|
}; |
||||||
|
|
||||||
|
static PyType_Spec PyUpb_ScalarMapContainer_Spec = { |
||||||
|
PYUPB_MODULE_NAME ".ScalarMapContainer", |
||||||
|
sizeof(PyUpb_MapContainer), |
||||||
|
0, |
||||||
|
Py_TPFLAGS_DEFAULT, |
||||||
|
PyUpb_ScalarMapContainer_Slots, |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// MessageMapContainer
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static PyMethodDef PyUpb_MessageMapContainer_Methods[] = { |
||||||
|
{"__contains__", PyUpb_MapContainer_Contains, METH_O, |
||||||
|
"Tests whether the map contains this element."}, |
||||||
|
{"clear", PyUpb_MapContainer_Clear, METH_NOARGS, |
||||||
|
"Removes all elements from the map."}, |
||||||
|
{"get", (PyCFunction)PyUpb_MapContainer_Get, METH_VARARGS | METH_KEYWORDS, |
||||||
|
"Gets the value for the given key if present, or otherwise a default"}, |
||||||
|
{"get_or_create", PyUpb_MapContainer_Subscript, METH_O, |
||||||
|
"Alias for getitem, useful to make explicit that the map is mutated."}, |
||||||
|
{"GetEntryClass", PyUpb_MapContainer_GetEntryClass, METH_NOARGS, |
||||||
|
"Return the class used to build Entries of (key, value) pairs."}, |
||||||
|
{"MergeFrom", PyUpb_MapContainer_MergeFrom, METH_O, |
||||||
|
"Merges a map into the current map."}, |
||||||
|
/*
|
||||||
|
{ "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, |
||||||
|
"Makes a deep copy of the class." }, |
||||||
|
{ "__reduce__", (PyCFunction)Reduce, METH_NOARGS, |
||||||
|
"Outputs picklable representation of the repeated field." }, |
||||||
|
*/ |
||||||
|
{NULL, NULL}, |
||||||
|
}; |
||||||
|
|
||||||
|
static PyType_Slot PyUpb_MessageMapContainer_Slots[] = { |
||||||
|
{Py_tp_dealloc, PyUpb_MapContainer_Dealloc}, |
||||||
|
{Py_mp_length, PyUpb_MapContainer_Length}, |
||||||
|
{Py_mp_subscript, PyUpb_MapContainer_Subscript}, |
||||||
|
{Py_mp_ass_subscript, PyUpb_MapContainer_AssignSubscript}, |
||||||
|
{Py_tp_methods, PyUpb_MessageMapContainer_Methods}, |
||||||
|
{Py_tp_iter, PyUpb_MapIterator_New}, |
||||||
|
{Py_tp_repr, PyUpb_MapContainer_Repr}, |
||||||
|
{Py_tp_hash, PyObject_HashNotImplemented}, |
||||||
|
{0, NULL}}; |
||||||
|
|
||||||
|
static PyType_Spec PyUpb_MessageMapContainer_Spec = { |
||||||
|
PYUPB_MODULE_NAME ".MessageMapContainer", sizeof(PyUpb_MapContainer), 0, |
||||||
|
Py_TPFLAGS_DEFAULT, PyUpb_MessageMapContainer_Slots}; |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// MapIterator
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct { |
||||||
|
PyObject_HEAD |
||||||
|
PyUpb_MapContainer* map; // We own a reference.
|
||||||
|
size_t iter; |
||||||
|
int version; |
||||||
|
} PyUpb_MapIterator; |
||||||
|
|
||||||
|
static PyObject* PyUpb_MapIterator_New(PyUpb_MapContainer* map) { |
||||||
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
||||||
|
PyUpb_MapIterator* iter = |
||||||
|
(void*)PyType_GenericAlloc(state->map_iterator_type, 0); |
||||||
|
iter->map = map; |
||||||
|
iter->iter = UPB_MAP_BEGIN; |
||||||
|
iter->version = map->version; |
||||||
|
Py_INCREF(map); |
||||||
|
return &iter->ob_base; |
||||||
|
} |
||||||
|
|
||||||
|
static void PyUpb_MapIterator_Dealloc(void* _self) { |
||||||
|
PyUpb_MapIterator* self = (PyUpb_MapIterator*)_self; |
||||||
|
Py_DECREF(&self->map->ob_base); |
||||||
|
PyUpb_Dealloc(_self); |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* PyUpb_MapIterator_IterNext(PyObject* _self) { |
||||||
|
PyUpb_MapIterator* self = (PyUpb_MapIterator*)_self; |
||||||
|
if (self->version != self->map->version) { |
||||||
|
return PyErr_Format(PyExc_RuntimeError, "Map modified during iteration."); |
||||||
|
} |
||||||
|
upb_map* map = PyUpb_MapContainer_GetIfWritable(self->map); |
||||||
|
if (!map) return NULL; |
||||||
|
if (!upb_mapiter_next(map, &self->iter)) return NULL; |
||||||
|
upb_msgval key = upb_mapiter_key(map, self->iter); |
||||||
|
const upb_fielddef* f = PyUpb_MapContainer_GetField(self->map); |
||||||
|
const upb_msgdef* entry_m = upb_fielddef_msgsubdef(f); |
||||||
|
const upb_fielddef* key_f = upb_msgdef_field(entry_m, 0); |
||||||
|
return PyUpb_UpbToPy(key, key_f, self->map->arena); |
||||||
|
} |
||||||
|
|
||||||
|
static PyType_Slot PyUpb_MapIterator_Slots[] = { |
||||||
|
{Py_tp_dealloc, PyUpb_MapIterator_Dealloc}, |
||||||
|
{Py_tp_iter, PyObject_SelfIter}, |
||||||
|
{Py_tp_iternext, PyUpb_MapIterator_IterNext}, |
||||||
|
{0, NULL}}; |
||||||
|
|
||||||
|
static PyType_Spec PyUpb_MapIterator_Spec = { |
||||||
|
PYUPB_MODULE_NAME ".MapIterator", sizeof(PyUpb_MapIterator), 0, |
||||||
|
Py_TPFLAGS_DEFAULT, PyUpb_MapIterator_Slots}; |
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Top Level
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static PyObject* GetMutableMappingBase(void) { |
||||||
|
PyObject* collections = NULL; |
||||||
|
PyObject* mapping = NULL; |
||||||
|
PyObject* bases = NULL; |
||||||
|
if ((collections = PyImport_ImportModule("collections.abc")) && |
||||||
|
(mapping = PyObject_GetAttrString(collections, "MutableMapping"))) { |
||||||
|
bases = Py_BuildValue("(O)", mapping); |
||||||
|
} |
||||||
|
Py_XDECREF(collections); |
||||||
|
Py_XDECREF(mapping); |
||||||
|
return bases; |
||||||
|
} |
||||||
|
|
||||||
|
bool PyUpb_Map_Init(PyObject* m) { |
||||||
|
PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m); |
||||||
|
PyObject* bases = GetMutableMappingBase(); |
||||||
|
if (!bases) return false; |
||||||
|
|
||||||
|
state->message_map_container_type = |
||||||
|
PyUpb_AddClassWithBases(m, &PyUpb_MessageMapContainer_Spec, bases); |
||||||
|
state->scalar_map_container_type = |
||||||
|
PyUpb_AddClassWithBases(m, &PyUpb_ScalarMapContainer_Spec, bases); |
||||||
|
state->map_iterator_type = PyUpb_AddClass(m, &PyUpb_MapIterator_Spec); |
||||||
|
|
||||||
|
Py_DECREF(bases); |
||||||
|
|
||||||
|
return state->message_map_container_type && |
||||||
|
state->scalar_map_container_type && state->map_iterator_type; |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/*
|
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef PYUPB_MAP_H__ |
||||||
|
#define PYUPB_MAP_H__ |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#include "python/python.h" |
||||||
|
#include "upb/def.h" |
||||||
|
|
||||||
|
PyObject* PyUpb_MapContainer_NewUnset(PyObject* parent, const upb_fielddef* f, |
||||||
|
PyObject* arena); |
||||||
|
PyObject* PyUpb_MapContainer_GetOrCreateWrapper(upb_map* u_map, |
||||||
|
const upb_fielddef* f, |
||||||
|
PyObject* arena); |
||||||
|
void PyUpb_MapContainer_SwitchToSet(PyObject* self, upb_map* map); |
||||||
|
int PyUpb_MapContainer_AssignSubscript(PyObject* self, PyObject* key, |
||||||
|
PyObject* val); |
||||||
|
void PyUpb_MapContainer_Invalidate(PyObject* obj); |
||||||
|
|
||||||
|
bool PyUpb_Map_Init(PyObject* m); |
||||||
|
|
||||||
|
#endif // PYUPB_MAP_H__
|
Loading…
Reference in new issue