Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add _PyDict_FromItems()
  • Loading branch information
methane committed Feb 27, 2022
commit e7d9ba07b22361413636b88580cfa70d17c2a4f5
4 changes: 4 additions & 0 deletions Include/internal/pycore_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ extern uint64_t _pydict_global_version;
#define DICT_NEXT_VERSION() (++_pydict_global_version)

PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values);
PyObject *_PyDict_FromItems(
PyObject *const *keys, Py_ssize_t keys_offset,
PyObject *const *values, Py_ssize_t values_offset,
Py_ssize_t length);

static inline void
_PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix)
Expand Down
19 changes: 2 additions & 17 deletions Objects/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,26 +935,11 @@ PyObject *
_PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
{
Py_ssize_t nkwargs;
PyObject *kwdict;
Py_ssize_t i;

assert(kwnames != NULL);
nkwargs = PyTuple_GET_SIZE(kwnames);
kwdict = _PyDict_NewPresized(nkwargs);
if (kwdict == NULL) {
return NULL;
}

for (i = 0; i < nkwargs; i++) {
PyObject *key = PyTuple_GET_ITEM(kwnames, i);
PyObject *value = *values++;
/* If key already exists, replace it with the new value */
if (PyDict_SetItem(kwdict, key, value)) {
Py_DECREF(kwdict);
return NULL;
}
}
return kwdict;
return _PyDict_FromItems(&PyTuple_GET_ITEM(kwnames, 0), 1,
values, 1, nkwargs);
}


Expand Down
55 changes: 50 additions & 5 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ As a consequence of this, split keys have a maximum size of 16.
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "stringlib/eq.h" // unicode_eq()

#include <stdbool.h>

/*[clinic input]
class dict "PyDictObject *" "&PyDict_Type"
[clinic start generated code]*/
Expand Down Expand Up @@ -600,7 +602,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)


static PyDictKeysObject*
new_keys_object(uint8_t log2_size, int unicode)
new_keys_object(uint8_t log2_size, bool unicode)
{
PyDictKeysObject *dk;
Py_ssize_t usable;
Expand Down Expand Up @@ -1561,8 +1563,8 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode)
return 0;
}

PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
static PyObject *
dict_new_presized(Py_ssize_t minused, bool unicode)
{
const uint8_t log2_max_presize = 17;
const Py_ssize_t max_presize = ((Py_ssize_t)1) << log2_max_presize;
Expand All @@ -1583,13 +1585,56 @@ _PyDict_NewPresized(Py_ssize_t minused)
log2_newsize = estimate_log2_keysize(minused);
}

// TODO: Document that this function returns dict optimized for string keys.
new_keys = new_keys_object(log2_newsize, 1);
new_keys = new_keys_object(log2_newsize, unicode);
if (new_keys == NULL)
return NULL;
return new_dict(new_keys, NULL, 0, 0);
}

PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
{
return dict_new_presized(minused, false);
}

PyObject *
_PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset,
PyObject *const *values, Py_ssize_t values_offset,
Py_ssize_t length)
{
bool unicode = true;
PyObject *const *ks = keys;

for (Py_ssize_t i = 0; i < length; i++) {
if (!PyUnicode_CheckExact(*ks)) {
unicode = false;
break;
}
ks += keys_offset;
}

PyObject *dict = dict_new_presized(length, unicode);
if (dict == NULL) {
return NULL;
}

ks = keys;
PyObject *const *vs = values;

for (Py_ssize_t i = 0; i < length; i++) {
PyObject *key = *ks;
PyObject *value = *vs;
if (PyDict_SetItem(dict, key, value) < 0) {
Py_DECREF(dict);
return NULL;
}
ks += keys_offset;
vs += values_offset;
}

return dict;
}

/* Note that, for historical reasons, PyDict_GetItem() suppresses all errors
* that may occur (originally dicts supported only string keys, and exceptions
* weren't possible). So, while the original intent was that a NULL return
Expand Down
31 changes: 7 additions & 24 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -3267,20 +3267,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}

TARGET(BUILD_MAP) {
Py_ssize_t i;
PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
PyObject *map = _PyDict_FromItems(
&PEEK(2*oparg), 2,
&PEEK(2*oparg - 1), 2,
oparg);
if (map == NULL)
goto error;
for (i = oparg; i > 0; i--) {
int err;
PyObject *key = PEEK(2*i);
PyObject *value = PEEK(2*i - 1);
err = PyDict_SetItem(map, key, value);
if (err != 0) {
Py_DECREF(map);
goto error;
}
}

while (oparg--) {
Py_DECREF(POP());
Expand Down Expand Up @@ -3346,7 +3338,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}

TARGET(BUILD_CONST_KEY_MAP) {
Py_ssize_t i;
PyObject *map;
PyObject *keys = TOP();
if (!PyTuple_CheckExact(keys) ||
Expand All @@ -3355,20 +3346,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
"bad BUILD_CONST_KEY_MAP keys argument");
goto error;
}
map = _PyDict_NewPresized((Py_ssize_t)oparg);
map = _PyDict_FromItems(
&PyTuple_GET_ITEM(keys, 0), 1,
&PEEK(oparg + 1), 1, oparg);
if (map == NULL) {
goto error;
}
for (i = oparg; i > 0; i--) {
int err;
PyObject *key = PyTuple_GET_ITEM(keys, oparg - i);
PyObject *value = PEEK(i + 1);
err = PyDict_SetItem(map, key, value);
if (err != 0) {
Py_DECREF(map);
goto error;
}
}

Py_DECREF(POP());
while (oparg--) {
Expand Down