Blame src/gen-use-table.py

Packit 874993
#!/usr/bin/python
Packit 874993
Packit 874993
import sys
Packit 874993
Packit 874993
if len (sys.argv) != 5:
Packit 874993
	print >>sys.stderr, "usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
Packit 874993
	sys.exit (1)
Packit 874993
Packit 874993
BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"]
Packit 874993
Packit 874993
files = [file (x) for x in sys.argv[1:]]
Packit 874993
Packit 874993
headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 2]
Packit 874993
headers.append (["UnicodeData.txt does not have a header."])
Packit 874993
Packit 874993
data = [{} for f in files]
Packit 874993
values = [{} for f in files]
Packit 874993
for i, f in enumerate (files):
Packit 874993
	for line in f:
Packit 874993
Packit 874993
		j = line.find ('#')
Packit 874993
		if j >= 0:
Packit 874993
			line = line[:j]
Packit 874993
Packit 874993
		fields = [x.strip () for x in line.split (';')]
Packit 874993
		if len (fields) == 1:
Packit 874993
			continue
Packit 874993
Packit 874993
		uu = fields[0].split ('..')
Packit 874993
		start = int (uu[0], 16)
Packit 874993
		if len (uu) == 1:
Packit 874993
			end = start
Packit 874993
		else:
Packit 874993
			end = int (uu[1], 16)
Packit 874993
Packit 874993
		t = fields[1 if i != 2 else 2]
Packit 874993
Packit 874993
		for u in range (start, end + 1):
Packit 874993
			data[i][u] = t
Packit 874993
		values[i][t] = values[i].get (t, 0) + end - start + 1
Packit 874993
Packit 874993
defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
Packit 874993
Packit 874993
# TODO Characters that are not in Unicode Indic files, but used in USE
Packit 874993
data[0][0x034F] = defaults[0]
Packit 874993
data[0][0x2060] = defaults[0]
Packit 874993
for u in range (0xFE00, 0xFE0F + 1):
Packit 874993
	data[0][u] = defaults[0]
Packit 874993
Packit 874993
# Merge data into one dict:
Packit 874993
for i,v in enumerate (defaults):
Packit 874993
	values[i][v] = values[i].get (v, 0) + 1
Packit 874993
combined = {}
Packit 874993
for i,d in enumerate (data):
Packit 874993
	for u,v in d.items ():
Packit 874993
		if i >= 2 and not u in combined:
Packit 874993
			continue
Packit 874993
		if not u in combined:
Packit 874993
			combined[u] = list (defaults)
Packit 874993
		combined[u][i] = v
Packit 874993
combined = {k:v for k,v in combined.items() if v[3] not in BLACKLISTED_BLOCKS}
Packit 874993
data = combined
Packit 874993
del combined
Packit 874993
num = len (data)
Packit 874993
Packit 874993
Packit 874993
property_names = [
Packit 874993
	# General_Category
Packit 874993
	'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc',
Packit 874993
	'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po',
Packit 874993
	'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',
Packit 874993
	# Indic_Syllabic_Category
Packit 874993
	'Other',
Packit 874993
	'Bindu',
Packit 874993
	'Visarga',
Packit 874993
	'Avagraha',
Packit 874993
	'Nukta',
Packit 874993
	'Virama',
Packit 874993
	'Pure_Killer',
Packit 874993
	'Invisible_Stacker',
Packit 874993
	'Vowel_Independent',
Packit 874993
	'Vowel_Dependent',
Packit 874993
	'Vowel',
Packit 874993
	'Consonant_Placeholder',
Packit 874993
	'Consonant',
Packit 874993
	'Consonant_Dead',
Packit 874993
	'Consonant_With_Stacker',
Packit 874993
	'Consonant_Prefixed',
Packit 874993
	'Consonant_Preceding_Repha',
Packit 874993
	'Consonant_Succeeding_Repha',
Packit 874993
	'Consonant_Subjoined',
Packit 874993
	'Consonant_Medial',
Packit 874993
	'Consonant_Final',
Packit 874993
	'Consonant_Head_Letter',
Packit 874993
	'Modifying_Letter',
Packit 874993
	'Tone_Letter',
Packit 874993
	'Tone_Mark',
Packit 874993
	'Gemination_Mark',
Packit 874993
	'Cantillation_Mark',
Packit 874993
	'Register_Shifter',
Packit 874993
	'Syllable_Modifier',
Packit 874993
	'Consonant_Killer',
Packit 874993
	'Non_Joiner',
Packit 874993
	'Joiner',
Packit 874993
	'Number_Joiner',
Packit 874993
	'Number',
Packit 874993
	'Brahmi_Joining_Number',
Packit 874993
	# Indic_Positional_Category
Packit 874993
	'Not_Applicable',
Packit 874993
	'Right',
Packit 874993
	'Left',
Packit 874993
	'Visual_Order_Left',
Packit 874993
	'Left_And_Right',
Packit 874993
	'Top',
Packit 874993
	'Bottom',
Packit 874993
	'Top_And_Bottom',
Packit 874993
	'Top_And_Right',
Packit 874993
	'Top_And_Left',
Packit 874993
	'Top_And_Left_And_Right',
Packit 874993
	'Bottom_And_Right',
Packit 874993
	'Top_And_Bottom_And_Right',
Packit 874993
	'Overstruck',
Packit 874993
]
Packit 874993
Packit 874993
class PropertyValue(object):
Packit 874993
	def __init__(self, name_):
Packit 874993
		self.name = name_
Packit 874993
	def __str__(self):
Packit 874993
		return self.name
Packit 874993
	def __eq__(self, other):
Packit 874993
		return self.name == (other if isinstance(other, basestring) else other.name)
Packit 874993
	def __ne__(self, other):
Packit 874993
		return not (self == other)
Packit 874993
Packit 874993
property_values = {}
Packit 874993
Packit 874993
for name in property_names:
Packit 874993
	value = PropertyValue(name)
Packit 874993
	assert value not in property_values
Packit 874993
	assert value not in globals()
Packit 874993
	property_values[name] = value
Packit 874993
globals().update(property_values)
Packit 874993
Packit 874993
Packit 874993
def is_BASE(U, UISC, UGC):
Packit 874993
	return (UISC in [Number, Consonant, Consonant_Head_Letter,
Packit 874993
			#SPEC-DRAFT Consonant_Placeholder,
Packit 874993
			Tone_Letter,
Packit 874993
			Vowel_Independent #SPEC-DRAFT
Packit 874993
			] or
Packit 874993
		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
Packit 874993
					Consonant_Subjoined, Vowel, Vowel_Dependent]))
Packit 874993
def is_BASE_IND(U, UISC, UGC):
Packit 874993
	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
Packit 874993
	return (UISC in [Consonant_Dead, Modifying_Letter] or
Packit 874993
		(UGC == Po and not U in [0x104E, 0x2022]) or
Packit 874993
		False # SPEC-DRAFT-OUTDATED! U == 0x002D
Packit 874993
		)
Packit 874993
def is_BASE_NUM(U, UISC, UGC):
Packit 874993
	return UISC == Brahmi_Joining_Number
Packit 874993
def is_BASE_OTHER(U, UISC, UGC):
Packit 874993
	if UISC == Consonant_Placeholder: return True #SPEC-DRAFT
Packit 874993
	#SPEC-DRAFT return U in [0x00A0, 0x00D7, 0x2015, 0x2022, 0x25CC, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
Packit 874993
	return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE]
Packit 874993
def is_CGJ(U, UISC, UGC):
Packit 874993
	return U == 0x034F
Packit 874993
def is_CONS_FINAL(U, UISC, UGC):
Packit 874993
	return ((UISC == Consonant_Final and UGC != Lo) or
Packit 874993
		UISC == Consonant_Succeeding_Repha)
Packit 874993
def is_CONS_FINAL_MOD(U, UISC, UGC):
Packit 874993
	#SPEC-DRAFT return  UISC in [Consonant_Final_Modifier, Syllable_Modifier]
Packit 874993
	return  UISC == Syllable_Modifier
Packit 874993
def is_CONS_MED(U, UISC, UGC):
Packit 874993
	return UISC == Consonant_Medial and UGC != Lo
Packit 874993
def is_CONS_MOD(U, UISC, UGC):
Packit 874993
	return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
Packit 874993
def is_CONS_SUB(U, UISC, UGC):
Packit 874993
	#SPEC-DRAFT return UISC == Consonant_Subjoined
Packit 874993
	return UISC == Consonant_Subjoined and UGC != Lo
Packit 874993
def is_HALANT(U, UISC, UGC):
Packit 874993
	return UISC in [Virama, Invisible_Stacker]
Packit 874993
def is_HALANT_NUM(U, UISC, UGC):
Packit 874993
	return UISC == Number_Joiner
Packit 874993
def is_ZWNJ(U, UISC, UGC):
Packit 874993
	return UISC == Non_Joiner
Packit 874993
def is_ZWJ(U, UISC, UGC):
Packit 874993
	return UISC == Joiner
Packit 874993
def is_Word_Joiner(U, UISC, UGC):
Packit 874993
	return U == 0x2060
Packit 874993
def is_OTHER(U, UISC, UGC):
Packit 874993
	#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
Packit 874993
	return (UISC == Other
Packit 874993
		and not is_SYM_MOD(U, UISC, UGC)
Packit 874993
		and not is_CGJ(U, UISC, UGC)
Packit 874993
		and not is_Word_Joiner(U, UISC, UGC)
Packit 874993
		and not is_VARIATION_SELECTOR(U, UISC, UGC)
Packit 874993
	)
Packit 874993
def is_Reserved(U, UISC, UGC):
Packit 874993
	return UGC == 'Cn'
Packit 874993
def is_REPHA(U, UISC, UGC):
Packit 874993
	#return UISC == Consonant_Preceding_Repha
Packit 874993
	#SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed
Packit 874993
	return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed]
Packit 874993
def is_SYM(U, UISC, UGC):
Packit 874993
	if U == 0x25CC: return False #SPEC-DRAFT
Packit 874993
	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
Packit 874993
	return UGC in [So, Sc]
Packit 874993
def is_SYM_MOD(U, UISC, UGC):
Packit 874993
	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
Packit 874993
def is_VARIATION_SELECTOR(U, UISC, UGC):
Packit 874993
	return 0xFE00 <= U <= 0xFE0F
Packit 874993
def is_VOWEL(U, UISC, UGC):
Packit 874993
	# https://github.com/roozbehp/unicode-data/issues/6
Packit 874993
	return (UISC == Pure_Killer or
Packit 874993
		(UGC != Lo and UISC in [Vowel, Vowel_Dependent] and U not in [0xAA29]))
Packit 874993
def is_VOWEL_MOD(U, UISC, UGC):
Packit 874993
	# https://github.com/roozbehp/unicode-data/issues/6
Packit 874993
	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
Packit 874993
		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
Packit 874993
Packit 874993
use_mapping = {
Packit 874993
	'B':	is_BASE,
Packit 874993
	'IND':	is_BASE_IND,
Packit 874993
	'N':	is_BASE_NUM,
Packit 874993
	'GB':	is_BASE_OTHER,
Packit 874993
	'CGJ':	is_CGJ,
Packit 874993
	'F':	is_CONS_FINAL,
Packit 874993
	'FM':	is_CONS_FINAL_MOD,
Packit 874993
	'M':	is_CONS_MED,
Packit 874993
	'CM':	is_CONS_MOD,
Packit 874993
	'SUB':	is_CONS_SUB,
Packit 874993
	'H':	is_HALANT,
Packit 874993
	'HN':	is_HALANT_NUM,
Packit 874993
	'ZWNJ':	is_ZWNJ,
Packit 874993
	'ZWJ':	is_ZWJ,
Packit 874993
	'WJ':	is_Word_Joiner,
Packit 874993
	'O':	is_OTHER,
Packit 874993
	'Rsv':	is_Reserved,
Packit 874993
	'R':	is_REPHA,
Packit 874993
	'S':	is_SYM,
Packit 874993
	'SM':	is_SYM_MOD,
Packit 874993
	'VS':	is_VARIATION_SELECTOR,
Packit 874993
	'V':	is_VOWEL,
Packit 874993
	'VM':	is_VOWEL_MOD,
Packit 874993
}
Packit 874993
Packit 874993
use_positions = {
Packit 874993
	'F': {
Packit 874993
		'Abv': [Top],
Packit 874993
		'Blw': [Bottom],
Packit 874993
		'Pst': [Right],
Packit 874993
	},
Packit 874993
	'M': {
Packit 874993
		'Abv': [Top],
Packit 874993
		'Blw': [Bottom],
Packit 874993
		'Pst': [Right],
Packit 874993
		'Pre': [Left],
Packit 874993
	},
Packit 874993
	'CM': {
Packit 874993
		'Abv': [Top],
Packit 874993
		'Blw': [Bottom],
Packit 874993
	},
Packit 874993
	'V': {
Packit 874993
		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
Packit 874993
		'Blw': [Bottom, Overstruck, Bottom_And_Right],
Packit 874993
		'Pst': [Right],
Packit 874993
		'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
Packit 874993
	},
Packit 874993
	'VM': {
Packit 874993
		'Abv': [Top],
Packit 874993
		'Blw': [Bottom, Overstruck],
Packit 874993
		'Pst': [Right],
Packit 874993
		'Pre': [Left],
Packit 874993
	},
Packit 874993
	'SM': {
Packit 874993
		'Abv': [Top],
Packit 874993
		'Blw': [Bottom],
Packit 874993
	},
Packit 874993
	'H': None,
Packit 874993
	'B': None,
Packit 874993
	'FM': None,
Packit 874993
	'SUB': None,
Packit 874993
}
Packit 874993
Packit 874993
def map_to_use(data):
Packit 874993
	out = {}
Packit 874993
	items = use_mapping.items()
Packit 874993
	for U,(UISC,UIPC,UGC,UBlock) in data.items():
Packit 874993
Packit 874993
		# Resolve Indic_Syllabic_Category
Packit 874993
Packit 874993
		# TODO: These don't have UISC assigned in Unicode 8.0, but
Packit 874993
		# have UIPC
Packit 874993
		if U == 0x17DD: UISC = Vowel_Dependent
Packit 874993
		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
Packit 874993
Packit 874993
		# TODO: U+1CED should only be allowed after some of
Packit 874993
		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
Packit 874993
		if U == 0x1CED: UISC = Tone_Mark
Packit 874993
Packit 874993
		evals = [(k, v(U,UISC,UGC)) for k,v in items]
Packit 874993
		values = [k for k,v in evals if v]
Packit 874993
		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
Packit 874993
		USE = values[0]
Packit 874993
Packit 874993
		# Resolve Indic_Positional_Category
Packit 874993
Packit 874993
		# TODO: Not in Unicode 8.0 yet, but in spec.
Packit 874993
		if U == 0x1B6C: UIPC = Bottom
Packit 874993
Packit 874993
		# TODO: These should die, but have UIPC in Unicode 8.0
Packit 874993
		if U in [0x953, 0x954]: UIPC = Not_Applicable
Packit 874993
Packit 874993
		# TODO: In USE's override list but not in Unicode 8.0
Packit 874993
		if U == 0x103C: UIPC = Left
Packit 874993
Packit 874993
		# TODO: These are not in USE's override list that we have, nor are they in Unicode 8.0
Packit 874993
		if 0xA926 <= U <= 0xA92A: UIPC = Top
Packit 874993
		if U == 0x111CA: UIPC = Bottom
Packit 874993
		if U == 0x11300: UIPC = Top
Packit 874993
		if U == 0x1133C: UIPC = Bottom
Packit 874993
		if U == 0x1171E: UIPC = Left # Correct?!
Packit 874993
		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
Packit 874993
		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
Packit 874993
Packit 874993
		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
Packit 874993
			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
Packit 874993
Packit 874993
		pos_mapping = use_positions.get(USE, None)
Packit 874993
		if pos_mapping:
Packit 874993
			values = [k for k,v in pos_mapping.items() if v and UIPC in v]
Packit 874993
			assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values)
Packit 874993
			USE = USE + values[0]
Packit 874993
Packit 874993
		out[U] = (USE, UBlock)
Packit 874993
	return out
Packit 874993
Packit 874993
defaults = ('O', 'No_Block')
Packit 874993
data = map_to_use(data)
Packit 874993
Packit 874993
# Remove the outliers
Packit 874993
singles = {}
Packit 874993
for u in [0x034F, 0x25CC, 0x1107F]:
Packit 874993
	singles[u] = data[u]
Packit 874993
	del data[u]
Packit 874993
Packit 874993
print "/* == Start of generated table == */"
Packit 874993
print "/*"
Packit 874993
print " * The following table is generated by running:"
Packit 874993
print " *"
Packit 874993
print " *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt"
Packit 874993
print " *"
Packit 874993
print " * on files with these headers:"
Packit 874993
print " *"
Packit 874993
for h in headers:
Packit 874993
	for l in h:
Packit 874993
		print " * %s" % (l.strip())
Packit 874993
print " */"
Packit 874993
print
Packit 874993
print '#include "hb-ot-shape-complex-use-private.hh"'
Packit 874993
print
Packit 874993
Packit 874993
total = 0
Packit 874993
used = 0
Packit 874993
last_block = None
Packit 874993
def print_block (block, start, end, data):
Packit 874993
	global total, used, last_block
Packit 874993
	if block and block != last_block:
Packit 874993
		print
Packit 874993
		print
Packit 874993
		print "  /* %s */" % block
Packit 874993
		if start % 16:
Packit 874993
			print ' ' * (20 + (start % 16 * 6)),
Packit 874993
	num = 0
Packit 874993
	assert start % 8 == 0
Packit 874993
	assert (end+1) % 8 == 0
Packit 874993
	for u in range (start, end+1):
Packit 874993
		if u % 16 == 0:
Packit 874993
			print
Packit 874993
			print "  /* %04X */" % u,
Packit 874993
		if u in data:
Packit 874993
			num += 1
Packit 874993
		d = data.get (u, defaults)
Packit 874993
		sys.stdout.write ("%6s," % d[0])
Packit 874993
Packit 874993
	total += end - start + 1
Packit 874993
	used += num
Packit 874993
	if block:
Packit 874993
		last_block = block
Packit 874993
Packit 874993
uu = data.keys ()
Packit 874993
uu.sort ()
Packit 874993
Packit 874993
last = -100000
Packit 874993
num = 0
Packit 874993
offset = 0
Packit 874993
starts = []
Packit 874993
ends = []
Packit 874993
for k,v in sorted(use_mapping.items()):
Packit 874993
	if k in use_positions and use_positions[k]: continue
Packit 874993
	print "#define %s	USE_%s	/* %s */" % (k, k, v.__name__[3:])
Packit 874993
for k,v in sorted(use_positions.items()):
Packit 874993
	if not v: continue
Packit 874993
	for suf in v.keys():
Packit 874993
		tag = k + suf
Packit 874993
		print "#define %s	USE_%s" % (tag, tag)
Packit 874993
print ""
Packit 874993
print "static const USE_TABLE_ELEMENT_TYPE use_table[] = {"
Packit 874993
for u in uu:
Packit 874993
	if u <= last:
Packit 874993
		continue
Packit 874993
	block = data[u][1]
Packit 874993
Packit 874993
	start = u//8*8
Packit 874993
	end = start+1
Packit 874993
	while end in uu and block == data[end][1]:
Packit 874993
		end += 1
Packit 874993
	end = (end-1)//8*8 + 7
Packit 874993
Packit 874993
	if start != last + 1:
Packit 874993
		if start - last <= 1+16*3:
Packit 874993
			print_block (None, last+1, start-1, data)
Packit 874993
			last = start-1
Packit 874993
		else:
Packit 874993
			if last >= 0:
Packit 874993
				ends.append (last + 1)
Packit 874993
				offset += ends[-1] - starts[-1]
Packit 874993
			print
Packit 874993
			print
Packit 874993
			print "#define use_offset_0x%04xu %d" % (start, offset)
Packit 874993
			starts.append (start)
Packit 874993
Packit 874993
	print_block (block, start, end, data)
Packit 874993
	last = end
Packit 874993
ends.append (last + 1)
Packit 874993
offset += ends[-1] - starts[-1]
Packit 874993
print
Packit 874993
print
Packit 874993
occupancy = used * 100. / total
Packit 874993
page_bits = 12
Packit 874993
print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)
Packit 874993
print
Packit 874993
print "USE_TABLE_ELEMENT_TYPE"
Packit 874993
print "hb_use_get_categories (hb_codepoint_t u)"
Packit 874993
print "{"
Packit 874993
print "  switch (u >> %d)" % page_bits
Packit 874993
print "  {"
Packit 874993
pages = set([u>>page_bits for u in starts+ends+singles.keys()])
Packit 874993
for p in sorted(pages):
Packit 874993
	print "    case 0x%0Xu:" % p
Packit 874993
	for (start,end) in zip (starts, ends):
Packit 874993
		if p not in [start>>page_bits, end>>page_bits]: continue
Packit 874993
		offset = "use_offset_0x%04xu" % start
Packit 874993
		print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
Packit 874993
	for u,d in singles.items ():
Packit 874993
		if p != u>>page_bits: continue
Packit 874993
		print "      if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0])
Packit 874993
	print "      break;"
Packit 874993
	print ""
Packit 874993
print "    default:"
Packit 874993
print "      break;"
Packit 874993
print "  }"
Packit 874993
print "  return USE_O;"
Packit 874993
print "}"
Packit 874993
print
Packit 874993
for k in sorted(use_mapping.keys()):
Packit 874993
	if k in use_positions and use_positions[k]: continue
Packit 874993
	print "#undef %s" % k
Packit 874993
for k,v in sorted(use_positions.items()):
Packit 874993
	if not v: continue
Packit 874993
	for suf in v.keys():
Packit 874993
		tag = k + suf
Packit 874993
		print "#undef %s" % tag
Packit 874993
print
Packit 874993
print "/* == End of generated table == */"
Packit 874993
Packit 874993
# Maintain at least 50% occupancy in the table */
Packit 874993
if occupancy < 50:
Packit 874993
	raise Exception ("Table too sparse, please investigate: ", occupancy)