Browse Source

new class: IndexedDict

- dict that keeps a list of keys for efficient lookup by index
MMGen 5 years ago
parent
commit
b15a295e6a
3 changed files with 98 additions and 5 deletions
  1. 30 0
      mmgen/obj.py
  2. 4 5
      mmgen/seed.py
  3. 64 0
      test/unit_tests_d/ut_indexed_dict.py

+ 30 - 0
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

+ 4 - 5
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)

+ 64 - 0
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