Thursday, November 12, 2009

Alpha-encoding file versions

When building installers the UpgradeVersion must have a unique property value that is an installer public property (upper-case alpha). So, what better way of adding uniqueness than making it have the form "product name + product version" with the version suitably encoded...

So, a script for turning a file version (4 x 16bit ints) encoded as a System.Version into a short alpha string, assuming that Major and Minor will be small, and that common approaches are to step Build, to use a stepped Build plus date-stamped Revision, or a timestamp Build and Revision --

from System import Version
def encode13(value, flag):
v = value + (13 if flag else 0)
return chr(ord('A')+v)
def encode25(value):
return chr(ord('A')+value)
def encodePrimary(value):
def encodePrimaryHelper(value, result):
digit = value % 13
rest = value /13
result += encode13(digit, rest)
if rest:
return encodePrimaryHelper(rest, result)
else:
return result
return encodePrimaryHelper(value, '')
def encodeByte(value):
if 0 == value:
return 'Z'
else:
return encode25(value/25)+encode25(value%25)
def encodeWord(value):
bottom = value % 256
top = (value/256) % 256
return encodeByte(top)+encodeByte(bottom)
def encodeVersion(version):
value = (encodePrimary(version.Major) +
encodePrimary(version.Minor) +
encodeWord(version.Build))
if version.Revision:
value += encodeWord(version.Revision)
return value
if __name__ == '__main__':
import unittest
class TestEncode(unittest.TestCase):
def testPrimary(self):
self.assertEqual(encodePrimary(0), 'A', 'encode 0')
self.assertEqual(encodePrimary(11), 'L', 'encode 11')
self.assertEqual(encodePrimary(13), 'NB', 'encode 13')
self.assertEqual(encodePrimary(32), 'TC', 'encode 32')
self.assertEqual(encodePrimary(169), 'NNB', 'encode 13')
def testByte(self):
self.assertEqual(encodeByte(0), 'Z', 'encode 0')
self.assertEqual(encodeByte(1), 'AB', 'encode 1')
self.assertEqual(encodeByte(25), 'BA', 'encode 25')
self.assertEqual(encodeByte(255), 'KF', 'encode 255')
def testVersion(self):
self.assertEqual(encodeVersion(Version('1.0.0.0')),
'BAZZ', 'encode 1.0.0.0')
self.assertEqual(encodeVersion(Version('1.0.0.1')),
'BAZZZAB', 'encode 1.0.0.1')
self.assertEqual(encodeVersion(Version('1.0.33.0')),
'BAZBI', 'encode 1.0.33.0')
self.assertEqual(encodeVersion(Version('1.0.3369.23350')),
'BAANBQDQCE', '1.0.3369.23350')
unittest.main()
view raw gistfile1.py hosted with ❤ by GitHub

where the first two facets are encoded as telescoped base-13 (with a bit to say "more to come"), and the second two are encoded as pairs of bytes -- Z for a zero byte or as a 2-character base-25 representation if non-zero, with a zero Revision being dropped. This gives 10 characters in a plausible worst case, or as low as 5 in some conventions (stepped build numbers only); as opposed to the naive 64-bit as all-base-26 which would give 11 characters always.

No comments :