From 27a722456eb9bae13b63f2fa1aba0b146702df5d Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 3 Aug 2025 13:45:48 -0500 Subject: [PATCH] scripts: Added support for SI-prefixes as iI punescape modifiers This adds %i and %I as punescape modifiers for limited printing of integers with SI prefixes: - %(field)i - base-10 SI prefixes - 100 => 100 - 10000 => 10K - 0.01 => 10m - %(field)I - base-2SI prefixes - 128 => 128 - 10240 => 10Ki - 0.125 => 128mi These can also easily include units as a part of the punescape string: - %(field)iops/s => 10Kops/s - %(field)IB => 10KiB This is particularly useful in plotmpl.py for adding explicit x/yticklabels without sacrificing the automatic SI-prefixes. --- scripts/codemap.py | 76 ++++++++++++++++++++++++++++++++++++++++-- scripts/codemapsvg.py | 74 +++++++++++++++++++++++++++++++++++++++-- scripts/csv.py | 77 +++++++++++++++++++++++++++++++++++++++++-- scripts/dbgbmap.py | 76 ++++++++++++++++++++++++++++++++++++++++-- scripts/dbgbmapsvg.py | 74 +++++++++++++++++++++++++++++++++++++++-- scripts/dbgtrace.py | 76 ++++++++++++++++++++++++++++++++++++++++-- scripts/plot.py | 12 +++++-- scripts/plotmpl.py | 10 ++++-- scripts/treemap.py | 76 ++++++++++++++++++++++++++++++++++++++++-- scripts/treemapsvg.py | 74 +++++++++++++++++++++++++++++++++++++++-- 10 files changed, 600 insertions(+), 25 deletions(-) diff --git a/scripts/codemap.py b/scripts/codemap.py index 66ae1ed3..0508a8f9 100755 --- a/scripts/codemap.py +++ b/scripts/codemap.py @@ -56,6 +56,38 @@ CODE_PATH = ['./scripts/code.py'] STACK_PATH = ['./scripts/stack.py'] CTX_PATH = ['./scripts/ctx.py'] +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -405,6 +437,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -415,7 +479,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -435,10 +499,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -456,7 +526,7 @@ def psplit(s): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') return [m.group() for m in re.finditer(pattern.pattern + '|.', s)] diff --git a/scripts/codemapsvg.py b/scripts/codemapsvg.py index 9be108e3..4500d762 100755 --- a/scripts/codemapsvg.py +++ b/scripts/codemapsvg.py @@ -58,6 +58,38 @@ CODE_PATH = ['./scripts/code.py'] STACK_PATH = ['./scripts/stack.py'] CTX_PATH = ['./scripts/ctx.py'] +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -267,6 +299,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -277,7 +341,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -297,10 +361,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) diff --git a/scripts/csv.py b/scripts/csv.py index 702de7fb..68a8bb67 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -24,6 +24,38 @@ import os import re import sys +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # various field types @@ -1274,6 +1306,37 @@ class CsvExpr: file=sys.stderr) sys.exit(3) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) # parse %-escaped strings # @@ -1285,7 +1348,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -1305,10 +1368,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -1330,6 +1399,10 @@ def punescape_help(): '%Uaaaaaaaa', 'A character with the hex value aaaaaaaa')) print(' %-21s %s' % ( '%(field)s', 'An existing field formatted as a string')) + print(' %-21s %s' % ( + '%(field)i', 'An field formatted with a base-10 SI prefix')) + print(' %-21s %s' % ( + '%(field)I', 'An field formatted with a base-2 SI prefix')) print(' %-21s %s' % ( '%(field)[dboxX]', 'An existing field formatted as an integer')) print(' %-21s %s' % ( diff --git a/scripts/dbgbmap.py b/scripts/dbgbmap.py index ad444290..7eaedc77 100755 --- a/scripts/dbgbmap.py +++ b/scripts/dbgbmap.py @@ -114,6 +114,38 @@ CHARS_BRAILLE = ( '⠉⢉⡉⣉⠩⢩⡩⣩⠍⢍⡍⣍⠭⢭⡭⣭' '⠙⢙⡙⣙⠹⢹⡹⣹⠝⢝⡝⣝⠽⢽⡽⣽' '⠋⢋⡋⣋⠫⢫⡫⣫⠏⢏⡏⣏⠯⢯⡯⣯' '⠛⢛⡛⣛⠻⢻⡻⣻⠟⢟⡟⣟⠿⢿⡿⣿') +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # some ways of block geometry representations # 512 -> 512 @@ -4023,6 +4055,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -4033,7 +4097,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -4053,10 +4117,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -4074,7 +4144,7 @@ def psplit(s): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') return [m.group() for m in re.finditer(pattern.pattern + '|.', s)] diff --git a/scripts/dbgbmapsvg.py b/scripts/dbgbmapsvg.py index 5aafc017..2f068f1b 100755 --- a/scripts/dbgbmapsvg.py +++ b/scripts/dbgbmapsvg.py @@ -133,6 +133,38 @@ HEIGHT = 350 FONT = ['sans-serif'] FONT_SIZE = 10 +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -3911,6 +3943,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -3921,7 +3985,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -3941,10 +4005,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) diff --git a/scripts/dbgtrace.py b/scripts/dbgtrace.py index d2a75336..b7e3580f 100755 --- a/scripts/dbgtrace.py +++ b/scripts/dbgtrace.py @@ -64,6 +64,38 @@ CHARS_BRAILLE = ( '⠉⢉⡉⣉⠩⢩⡩⣩⠍⢍⡍⣍⠭⢭⡭⣭' '⠙⢙⡙⣙⠹⢹⡹⣹⠝⢝⡝⣝⠽⢽⡽⣽' '⠋⢋⡋⣋⠫⢫⡫⣫⠏⢏⡏⣏⠯⢯⡯⣯' '⠛⢛⡛⣛⠻⢻⡻⣻⠟⢟⡟⣟⠿⢿⡿⣿') +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -413,6 +445,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -423,7 +487,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -443,10 +507,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -464,7 +534,7 @@ def psplit(s): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') return [m.group() for m in re.finditer(pattern.pattern + '|.', s)] diff --git a/scripts/plot.py b/scripts/plot.py index a2b49443..cbe0f1ac 100755 --- a/scripts/plot.py +++ b/scripts/plot.py @@ -614,7 +614,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -634,10 +634,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -655,7 +661,7 @@ def psplit(s): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') return [m.group() for m in re.finditer(pattern.pattern + '|.', s)] diff --git a/scripts/plotmpl.py b/scripts/plotmpl.py index baa96f8a..1de5e0b4 100755 --- a/scripts/plotmpl.py +++ b/scripts/plotmpl.py @@ -498,7 +498,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -518,10 +518,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) diff --git a/scripts/treemap.py b/scripts/treemap.py index 3b0b7d31..6e0b7ead 100755 --- a/scripts/treemap.py +++ b/scripts/treemap.py @@ -48,6 +48,38 @@ CHARS_BRAILLE = ( '⠉⢉⡉⣉⠩⢩⡩⣩⠍⢍⡍⣍⠭⢭⡭⣭' '⠙⢙⡙⣙⠹⢹⡹⣹⠝⢝⡝⣝⠽⢽⡽⣽' '⠋⢋⡋⣋⠫⢫⡫⣫⠏⢏⡏⣏⠯⢯⡯⣯' '⠛⢛⡛⣛⠻⢻⡻⣻⠟⢟⡟⣟⠿⢿⡿⣿') +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -478,6 +510,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -488,7 +552,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -508,10 +572,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v) @@ -529,7 +599,7 @@ def psplit(s): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') return [m.group() for m in re.finditer(pattern.pattern + '|.', s)] diff --git a/scripts/treemapsvg.py b/scripts/treemapsvg.py index 63ac4029..09135d43 100755 --- a/scripts/treemapsvg.py +++ b/scripts/treemapsvg.py @@ -50,6 +50,38 @@ HEIGHT = 350 FONT = ['sans-serif'] FONT_SIZE = 10 +SI_PREFIXES = { + 18: 'E', + 15: 'P', + 12: 'T', + 9: 'G', + 6: 'M', + 3: 'K', + 0: '', + -3: 'm', + -6: 'u', + -9: 'n', + -12: 'p', + -15: 'f', + -18: 'a', +} + +SI2_PREFIXES = { + 60: 'Ei', + 50: 'Pi', + 40: 'Ti', + 30: 'Gi', + 20: 'Mi', + 10: 'Ki', + 0: '', + -10: 'mi', + -20: 'ui', + -30: 'ni', + -40: 'pi', + -50: 'fi', + -60: 'ai', +} + # open with '-' for stdin/stdout def openio(path, mode='r', buffering=-1): @@ -339,6 +371,38 @@ class CsvAttr: return len(self.keyed) +# SI-prefix formatter +def si(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 3*mt.floor(mt.log(abs(x), 10**3)) + p = min(18, max(-18, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (10.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI_PREFIXES[p]) + +# SI-prefix formatter for powers-of-two +def si2(x): + if x == 0: + return '0' + # figure out prefix and scale + p = 10*mt.floor(mt.log(abs(x), 2**10)) + p = min(30, max(-30, p)) + # format with 3 digits of precision + s = '%.3f' % (abs(x) / (2.0**p)) + s = s[:3+1] + # truncate but only digits that follow the dot + if '.' in s: + s = s.rstrip('0') + s = s.rstrip('.') + return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p]) + # parse %-escaped strings # # attrs can override __getitem__ for lazy attr generation @@ -349,7 +413,7 @@ def punescape(s, attrs=None): '|' '%u....' '|' '%U........' '|' '%\((?P[^)]*)\)' - '(?P[+\- #0-9\.]*[sdboxXfFeEgG])') + '(?P[+\- #0-9\.]*[siIdboxXfFeEgG])') def unescape(m): if m.group()[1] == '%': return '%' elif m.group()[1] == 'n': return '\n' @@ -369,10 +433,16 @@ def punescape(s, attrs=None): if isinstance(v, str): v = dat(v, 0) v = int(v) - elif f[-1] in 'fFeEgG': + elif f[-1] in 'iIfFeEgG': if isinstance(v, str): v = dat(v, 0) v = float(v) + if f[-1] in 'iI': + v = (si if 'i' in f[-1] else si2)(v) + f = f.replace('i', 's').replace('I', 's') + if '+' in f and not v.startswith('-'): + v = '+'+v + f = f.replace('+', '').replace('-', '') else: f = ('<' if '-' in f else '>') + f.replace('-', '') v = str(v)