From b15a295e6adf96e1dda8fd7e86e6943676096637 Mon Sep 17 00:00:00 2001 From: MMGen Date: Wed, 5 Jun 2019 14:06:03 +0000 Subject: [PATCH] new class: IndexedDict - dict that keeps a list of keys for efficient lookup by index --- mmgen/obj.py | 30 +++++++++++++ mmgen/seed.py | 9 ++-- test/unit_tests_d/ut_indexed_dict.py | 64 ++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100755 test/unit_tests_d/ut_indexed_dict.py diff --git a/mmgen/obj.py b/mmgen/obj.py index b7985d16..ced0bdbb 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -112,6 +112,36 @@ class MMGenObject(object): import re return re.sub('\n+','\n',''.join(out)) +# dict that keeps a list of keys for efficient lookup by index +class IndexedDict(dict): + + def __init__(self,*args,**kwargs): + if args or kwargs: + self.die('initializing values via constructor') + self.__keylist = [] + return dict.__init__(self,*args,**kwargs) + + def __setitem__(self,key,value): + if key in self: + self.die('reassignment to existing key') + self.__keylist.append(key) + return dict.__setitem__(self,key,value) + + @property + def keys(self): + return self.__keylist + + def key(self,idx): + return self.__keylist[idx] + + def __delitem__(self,*args): self.die('item deletion') + def move_to_end(self,*args): self.die('item moving') + def clear(self,*args): self.die('clearing') + def update(self,*args): self.die('updating') + + def die(self,desc): + raise NotImplementedError('{} not implemented for type {}'.format(desc,type(self).__name__)) + class MMGenList(list,MMGenObject): pass class MMGenDict(dict,MMGenObject): pass class AddrListList(list,MMGenObject): pass diff --git a/mmgen/seed.py b/mmgen/seed.py index 2be244f6..42fd2226 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -70,14 +70,13 @@ class SubSeedList(MMGenObject): def __init__(self,parent_seed): self.member_type = SubSeed self.parent_seed = parent_seed - from collections import OrderedDict - self.data = { 'long': OrderedDict(), 'short': OrderedDict() } + self.data = { 'long': IndexedDict(), 'short': IndexedDict() } def __len__(self): return len(self.data['long']) def get_params_by_ss_idx(self,ss_idx): - sid = list(self.data[ss_idx.type].keys())[ss_idx.idx-1] + sid = self.data[ss_idx.type].key(ss_idx.idx-1) idx,nonce = self.data[ss_idx.type][sid] return (sid,idx,nonce) @@ -184,8 +183,8 @@ class SubSeedList(MMGenObject): hdr += fs1.format('Long Subseeds','Short Subseeds') hdr += fs1.format('-------------','--------------') - sl = tuple(self.data['long']) - ss = tuple(self.data['short']) + sl = self.data['long'].keys + ss = self.data['short'].keys body = (fs2.format(sl[n-1],ss[n-1],i=n) for n in r.iterate()) return hdr + ''.join(body) diff --git a/test/unit_tests_d/ut_indexed_dict.py b/test/unit_tests_d/ut_indexed_dict.py new file mode 100755 index 00000000..cb6596cd --- /dev/null +++ b/test/unit_tests_d/ut_indexed_dict.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +test/unit_tests_d/ut_indexed_dict: IndexedDict class unit test for the MMGen suite +""" + +from mmgen.common import * + +class unit_test(object): + + def run_test(self,name): + bad_msg = ( 'initializing values via constructor', + 'reassignment to existing key', + 'item deletion', + 'item moving', + 'clearing', + 'updating' ) + def bad0(): IndexedDict(arg) + def bad1(): d['a'] = 2 + def bad2(): del d['a'] + def bad3(): d.move_to_end('a') + def bad4(): d.clear() + def bad5(): d.update(d) + + def odie(n): rdie(3,"\nillegal action '{}' failed to raise exception".format(bad_msg[n])) + def omsg(e): vmsg(' - ' + e.args[0]) + + msg_r('Testing class IndexedDict...') + + from mmgen.obj import IndexedDict + d = IndexedDict() + + d['a'] = 1 + d['b'] = 2 + + vmsg('\nChecking error handling:') + + arg = [('a',1),('b',2)] + dict(arg) + + for i in range(6): + try: locals()['bad'+str(i)]() + except NotImplementedError as e: omsg(e) + else: odie(i) + + try: d.key(2) + except Exception as e: omsg(e) + else: odie('list index out of range') + + d['c'] = 3 + + d_chk = {'a':1,'b':2,'c':3} + assert d == d_chk, d + + d_keys_chk = ['a','b','c'] + assert d.keys == d_keys_chk, d.keys + + A = d.key(0) + assert A == 'a', A + + A = d.key(2) + assert A == 'c', A + + msg('OK') + return True