Skip to content

Commit

Permalink
try to avoid malloc in _update
Browse files Browse the repository at this point in the history
  • Loading branch information
jab committed May 6, 2016
1 parent eb7df55 commit 6f9edc5
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 2 deletions.
34 changes: 33 additions & 1 deletion bidict/_common.py
Expand Up @@ -4,9 +4,10 @@
Also provides related exception classes and collision behaviors.
"""

from .compat import PY2, iteritems, viewkeys
from .compat import PY2, ifilter, iterkeys, iteritems, itervalues, viewkeys
from .util import pairs
from collections import Mapping
from itertools import chain


def _proxied(methodname, ivarname='_fwd', doc=None):
Expand Down Expand Up @@ -131,6 +132,37 @@ def _update(self, on_key_coll, on_val_coll, *args, **kw):
_fwd = self._fwd
_inv = self._inv
missing = object()

# Optimization: Try to detect duplicate keys and values early
# before doing any mallocs.
arg0 = args[0] if args else {}
if isinstance(arg0, Mapping):
if on_key_coll is RAISE:
if arg0 and kw:
# New mappings in both arg0 and kw ->
# Check if new key given twice with different values.
d1, d2 = (arg0, kw) if len(arg0) < len(kw) else (kw, arg0)
for (k, v) in iteritems(d1):
v2 = d2.get(k, missing)
if v2 is not missing and v2 != v:
raise KeyNotUniqueError(k)
# Check if a new key duplicates an existing key.
dupk = next(ifilter(_fwd.__contains__,
chain(iterkeys(arg0), iterkeys(kw))), missing)
if dupk is not missing:
raise KeyExistsError((dupk, _fwd[dupk]))
if on_val_coll is RAISE:
# Want to check if a new value was given twice with different keys,
# but there's no way to do this in O(n) time without a malloc.
# So skip checking this here; we'll catch it below.
# ---
# Check if a new value duplicates an existing value.
dupv = next(ifilter(_inv.__contains__,
chain(itervalues(arg0), itervalues(kw))), missing)
if dupv is not missing:
raise ValueExistsError((_inv[dupv], dupv))
# End optimization.

updatefwd = self._dcls()
updateinv = self._dcls()

Expand Down
4 changes: 3 additions & 1 deletion bidict/compat.py
Expand Up @@ -57,13 +57,15 @@
iterkeys = methodcaller('iterkeys')
itervalues = methodcaller('itervalues')
iteritems = methodcaller('iteritems')
from itertools import izip, izip_longest
from itertools import ifilter, imap, izip, izip_longest
else: # pragma: no cover
viewkeys = methodcaller('keys')
viewvalues = methodcaller('values')
viewitems = methodcaller('items')
iterkeys = _compose(iter, viewkeys)
itervalues = _compose(iter, viewvalues)
iteritems = _compose(iter, viewitems)
ifilter = filter
imap = map
izip = zip
from itertools import zip_longest as izip_longest

0 comments on commit 6f9edc5

Please sign in to comment.