Fixed a Python test by adding a new map insert function that distinguishes between insert and update.
PiperOrigin-RevId: 447214618
diff --git a/python/BUILD b/python/BUILD
index 5db0894..3bb4a1e 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -203,6 +203,7 @@
"-Wno-pedantic",
],
deps = [
+ "//:collections",
"//:descriptor_upb_proto_reflection",
"//:reflection",
"//:table",
diff --git a/python/map.c b/python/map.c
index 35d3d32..482e62f 100644
--- a/python/map.c
+++ b/python/map.c
@@ -30,6 +30,7 @@
#include "python/convert.h"
#include "python/message.h"
#include "python/protobuf.h"
+#include "upb/collections.h"
// -----------------------------------------------------------------------------
// MapContainer
@@ -145,6 +146,21 @@
return map;
}
+bool PyUpb_MapContainer_Set(PyUpb_MapContainer* self, upb_Map* map,
+ upb_MessageValue key, upb_MessageValue val,
+ upb_Arena* arena) {
+ switch (upb_Map_Insert(map, key, val, arena)) {
+ case kUpb_MapInsertStatus_Inserted:
+ return true;
+ case kUpb_MapInsertStatus_Replaced:
+ // We did not insert a new key, undo the previous invalidate.
+ self->version--;
+ return true;
+ case kUpb_MapInsertStatus_OutOfMemory:
+ return false;
+ }
+}
+
int PyUpb_MapContainer_AssignSubscript(PyObject* _self, PyObject* key,
PyObject* val) {
PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
@@ -159,7 +175,7 @@
if (val) {
if (!PyUpb_PyToUpb(val, val_f, &u_val, arena)) return -1;
- upb_Map_Set(map, u_key, u_val, arena);
+ if (!PyUpb_MapContainer_Set(self, map, u_key, u_val, arena)) return -1;
} else {
if (!upb_Map_Delete(map, u_key)) {
PyErr_Format(PyExc_KeyError, "Key not present in map");
@@ -187,7 +203,7 @@
} else {
memset(&u_val, 0, sizeof(u_val));
}
- upb_Map_Set(map, u_key, u_val, arena);
+ if (!PyUpb_MapContainer_Set(self, map, u_key, u_val, arena)) return false;
}
return PyUpb_UpbToPy(u_val, val_f, self->arena);
}
diff --git a/python/pb_unit_tests/message_test_wrapper.py b/python/pb_unit_tests/message_test_wrapper.py
index 040ffde..bb1b6c7 100644
--- a/python/pb_unit_tests/message_test_wrapper.py
+++ b/python/pb_unit_tests/message_test_wrapper.py
@@ -47,7 +47,6 @@
Proto3Test.testMergeFromBadType.__unittest_expecting_failure__ = True
Proto2Test.test_documentation.__unittest_expecting_failure__ = True
-Proto3Test.testModifyMapEntryWhileIterating.__unittest_expecting_failure__ = True
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/upb/collections.c b/upb/collections.c
index a0de040..6bf7fbb 100644
--- a/upb/collections.c
+++ b/upb/collections.c
@@ -147,9 +147,10 @@
void upb_Map_Clear(upb_Map* map) { _upb_Map_Clear(map); }
-bool upb_Map_Set(upb_Map* map, upb_MessageValue key, upb_MessageValue val,
- upb_Arena* arena) {
- return _upb_Map_Set(map, &key, map->key_size, &val, map->val_size, arena);
+upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key,
+ upb_MessageValue val, upb_Arena* arena) {
+ return (upb_MapInsertStatus)_upb_Map_Insert(map, &key, map->key_size, &val,
+ map->val_size, arena);
}
bool upb_Map_Delete(upb_Map* map, upb_MessageValue key) {
diff --git a/upb/collections.h b/upb/collections.h
index fa28d83..047e14e 100644
--- a/upb/collections.h
+++ b/upb/collections.h
@@ -110,10 +110,28 @@
/* Removes all entries in the map. */
void upb_Map_Clear(upb_Map* map);
-/* Sets the given key to the given value. Returns true if this was a new key in
- * the map, or false if an existing key was replaced. */
-bool upb_Map_Set(upb_Map* map, upb_MessageValue key, upb_MessageValue val,
- upb_Arena* arena);
+typedef enum {
+ // LINT.IfChange
+ kUpb_MapInsertStatus_Inserted = 0,
+ kUpb_MapInsertStatus_Replaced = 1,
+ kUpb_MapInsertStatus_OutOfMemory = 2,
+ // LINT.ThenChange(//depot/google3/third_party/upb/upb/msg_internal.h)
+} upb_MapInsertStatus;
+
+/* Sets the given key to the given value, returning whether the key was inserted
+ * or replaced. If the key was inserted, then any existing iterators will be
+ * invalidated. */
+upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key,
+ upb_MessageValue val, upb_Arena* arena);
+
+/* Sets the given key to the given value. Returns false if memory allocation
+ * failed. If the key is newly inserted, then any existing iterators will be
+ * invalidated. */
+UPB_INLINE bool upb_Map_Set(upb_Map* map, upb_MessageValue key,
+ upb_MessageValue val, upb_Arena* arena) {
+ return upb_Map_Insert(map, key, val, arena) !=
+ kUpb_MapInsertStatus_OutOfMemory;
+}
/* Deletes this key from the table. Returns true if the key was present. */
bool upb_Map_Delete(upb_Map* map, upb_MessageValue key);
diff --git a/upb/decode.c b/upb/decode.c
index 0985abc..e6dac27 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -653,7 +653,10 @@
decode_err(d, kUpb_DecodeStatus_OutOfMemory);
}
} else {
- _upb_Map_Set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena);
+ if (_upb_Map_Insert(map, &ent.k, map->key_size, &ent.v, map->val_size,
+ &d->arena) == _kUpb_MapInsertStatus_OutOfMemory) {
+ decode_err(d, kUpb_DecodeStatus_OutOfMemory);
+ }
}
return ptr;
}
diff --git a/upb/msg_internal.h b/upb/msg_internal.h
index b5d8b5c..584ed77 100644
--- a/upb/msg_internal.h
+++ b/upb/msg_internal.h
@@ -667,15 +667,31 @@
return (void*)str_tabent(&it);
}
-UPB_INLINE bool _upb_Map_Set(upb_Map* map, const void* key, size_t key_size,
- void* val, size_t val_size, upb_Arena* a) {
+typedef enum {
+ // LINT.IfChange
+ _kUpb_MapInsertStatus_Inserted = 0,
+ _kUpb_MapInsertStatus_Replaced = 1,
+ _kUpb_MapInsertStatus_OutOfMemory = 2,
+ // LINT.ThenChange(//depot/google3/third_party/upb/upb/collections.h)
+} _upb_MapInsertStatus;
+
+UPB_INLINE _upb_MapInsertStatus _upb_Map_Insert(upb_Map* map, const void* key,
+ size_t key_size, void* val,
+ size_t val_size, upb_Arena* a) {
upb_StringView strkey = _upb_map_tokey(key, key_size);
upb_value tabval = {0};
- if (!_upb_map_tovalue(val, val_size, &tabval, a)) return false;
+ if (!_upb_map_tovalue(val, val_size, &tabval, a)) {
+ return _kUpb_MapInsertStatus_OutOfMemory;
+ }
/* TODO(haberman): add overwrite operation to minimize number of lookups. */
- upb_strtable_remove2(&map->table, strkey.data, strkey.size, NULL);
- return upb_strtable_insert(&map->table, strkey.data, strkey.size, tabval, a);
+ bool removed =
+ upb_strtable_remove2(&map->table, strkey.data, strkey.size, NULL);
+ if (!upb_strtable_insert(&map->table, strkey.data, strkey.size, tabval, a)) {
+ return _kUpb_MapInsertStatus_OutOfMemory;
+ }
+ return removed ? _kUpb_MapInsertStatus_Replaced
+ : _kUpb_MapInsertStatus_Inserted;
}
UPB_INLINE bool _upb_Map_Delete(upb_Map* map, const void* key,
@@ -717,7 +733,8 @@
if (!*map) {
*map = _upb_Map_New(arena, key_size, val_size);
}
- return _upb_Map_Set(*map, key, key_size, val, val_size, arena);
+ return _upb_Map_Insert(*map, key, key_size, val, val_size, arena) !=
+ _kUpb_MapInsertStatus_OutOfMemory;
}
UPB_INLINE bool _upb_msg_map_delete(upb_Message* msg, size_t ofs,