scripts: maps: Cleaned up comments and junk

This took a bit of a messy route, but these scripts should be good to go
now.
This commit is contained in:
Christopher Haster
2025-04-11 02:58:00 -05:00
parent 50f652d44f
commit 5e817be9cc
3 changed files with 0 additions and 1557 deletions
-1
View File
@@ -2733,7 +2733,6 @@ class Gstate:
locals()[g.__name__.lower()] = ft.cached_property(_parser(g))
# TODO sync
# high-level littlefs representation
class Lfs:
def __init__(self, bd, mtree, config=None, gstate=None, cksum=None, *,
-617
View File
@@ -3851,8 +3851,6 @@ def punescape(s, attrs=None):
return re.sub(pattern, unescape, s)
# TODO sync these
# naive space filling curve (the default)
def naive_curve(width, height):
for y in range(height):
@@ -4065,580 +4063,6 @@ class BmapBlock:
}
# a mergable range set type
class RangeSet:
def __init__(self, ranges=None):
self._ranges = []
if ranges is not None:
# using add here makes sure all ranges are merged/sorted
# correctly
for r in ranges:
self.add(r)
def __repr__(self):
return 'RangeSet(%r)' % self._ranges
def __contains__(self, k):
i = bisect.bisect(self._ranges, k,
key=lambda r: r.start) - 1
if i > -1:
return k in self._ranges[i]
else:
return False
def __bool__(self):
return bool(self._ranges)
def ranges(self):
yield from self._ranges
def __iter__(self):
for r in self._ranges:
yield from r
def add(self, r):
assert isinstance(r, range)
# trivial range?
if not r:
return
# find earliest possible merge point
ranges = self._ranges
i = bisect.bisect_left(ranges, r.start,
key=lambda r: r.stop)
# copy ranges < merge
merged = ranges[:i]
# merge ranges and append
while i < len(ranges) and ranges[i].start <= r.stop:
r = range(
min(ranges[i].start, r.start),
max(ranges[i].stop, r.stop))
i += 1
merged.append(r)
# copy ranges > merge
merged.extend(ranges[i:])
self._ranges = merged
def remove(self, r):
assert isinstance(r, range)
# trivial range?
if not r:
return
# find earliest possible carve point
ranges = self._ranges
i = bisect.bisect_left(ranges, r.start,
key=lambda r: r.stop)
# copy ranges < carve
carved = ranges[:i]
# carve overlapping ranges, note this can split ranges
while i < len(ranges) and ranges[i].start <= r.stop:
if ranges[i].start < r.start:
carved.append(range(ranges[i].start, r.start))
if ranges[i].stop > r.stop:
carved.append(range(r.stop, ranges[i].stop))
i += 1
# copy ranges > carve
carved.extend(ranges[i:])
self._ranges = carved
@property
def start(self):
if not self._ranges:
return 0
else:
return self._ranges[0].start
@property
def stop(self):
if not self._ranges:
return 0
else:
return self._ranges[-1].stop
def __len__(self):
return self.stop
def copy(self):
# create a shallow copy
ranges = RangeSet()
ranges._ranges = self._ranges.copy()
return ranges
def __getitem__(self, slice_):
assert isinstance(slice_, slice)
# create a copy
ranges = self.copy()
# just use remove to do the carving, it's good enough probably
if slice_.stop is not None:
ranges.remove(range(slice_.stop, len(self)))
if slice_.start is not None:
ranges.remove(range(0, slice_.start))
ranges._ranges = [range(
r.start - slice_.start,
r.stop - slice_.start)
for r in ranges._ranges]
return ranges
def __ior__(self, other):
for r in other.ranges():
self.add(r)
return self
def __or__(self, other):
ranges = self.copy()
ranges |= other
return ranges
# a mergable range dict type
class RangeDict:
def __init__(self, ranges=None):
self._ranges = []
if ranges is not None:
# using __setitem__ here makes sure all ranges are
# merged/sorted correctly
for r, v in ranges:
self[r] = v
def __repr__(self):
return 'RangeDict(%r)' % self._ranges
def _get(self, k):
i = bisect.bisect(self._ranges, k,
key=lambda rv: rv[0].start) - 1
if i > -1:
return self._ranges[i][1]
else:
raise KeyError(k)
def get(self, k, d=None):
try:
return self._get(k)
except KeyError:
return d
def __getitem__(self, k):
# special case for slicing
if isinstance(k, slice):
return self._slice(k)
else:
return self._get(k)
def __contains__(self, k):
try:
self._get(k)
return True
except KeyError:
return False
def __bool__(self):
return bool(self._ranges)
def ranges(self):
yield from self._ranges
def __iter__(self):
for r, v in self._ranges:
for k in r:
yield k, v
# apply a function to a range
def map(self, r, f):
assert isinstance(r, range)
# trivial range?
if not r:
return
# find earliest possible merge point
ranges = self._ranges
i = bisect.bisect_left(ranges, r.start,
key=lambda rv: rv[0].stop)
# copy ranges < merge
merged = ranges[:i]
# map, merge/carve ranges
while i < len(ranges) and ranges[i][0].start <= r.stop:
# carve prefix
if ranges[i][0].start < r.start:
merged.append((
range(ranges[i][0].start, r.start),
ranges[i][1]))
# need fill?
if ranges[i][0].start > r.start:
v = f(None)
# merge?
if (merged
and merged[-1][0].stop >= r.start
and merged[-1][1] == v):
merged[-1] = (
range(
merged[-1][0].start,
ranges[i][0].start),
merged[-1][1])
else:
merged.append((
range(r.start, ranges[i][0].start),
f(None)))
# apply f
v = f(ranges[i][1])
# merge?
if (merged
and merged[-1][0].stop >= r.start
and merged[-1][1] == v):
merged[-1] = (
range(
merged[-1][0].start,
min(ranges[i][0].stop, r.stop)),
merged[-1][1])
else:
merged.append((
range(
r.start,
min(ranges[i][0].stop, r.stop)),
v))
r = range(
min(ranges[i][0].stop, r.stop),
r.stop)
# carve suffix
if ranges[i][0].stop > r.stop:
# merge?
if ranges[i][1] == v:
merged[-1] = (
range(
merged[-1][0].start,
ranges[i][0].stop),
merged[-1][1])
else:
merged.append((
range(r.stop, ranges[i][0].stop),
ranges[i][1]))
i += 1
# need fill?
if not merged or merged[-1][0].stop < r.stop:
v = f(None)
# merge?
if (merged
and merged[-1][0].stop >= r.start
and merged[-1][1] == v):
merged[-1] = (
range(merged[-1][0].start, r.stop),
merged[-1][1])
else:
merged.append((r, v))
# copy ranges > merge
merged.extend(ranges[i:])
self._ranges = merged
def __setitem__(self, r, v):
# we can define __setitem__ using map
assert isinstance(r, range)
self.map(r, lambda _: v)
def __delitem__(self, r):
# __delitem__ is a bit more complicated
assert isinstance(r, range)
# trivial range?
if not r:
return
# find earliest possible carve point
ranges = self._ranges
i = bisect.bisect_left(ranges, r.start,
key=lambda rv: rv[0].stop)
# copy ranges < carve
carved = ranges[:i]
# carve overlapping ranges, note this can split ranges
while i < len(ranges) and ranges[i][0].start <= r.stop:
if ranges[i][0].start < r.start:
carved.append((
range(ranges[i][0].start, r.start),
ranges[i][1]))
if ranges[i][0].stop > r.stop:
carved.append((
range(r.stop, ranges[i][0].stop),
ranges[i][1]))
i += 1
# copy ranges > carve
carved.extend(ranges[i:])
self._ranges = carved
@property
def start(self):
if not self._ranges:
return 0
else:
return self._ranges[0][0].start
@property
def stop(self):
if not self._ranges:
return 0
else:
return self._ranges[-1][0].stop
def __len__(self):
return self.stop
def copy(self):
# create a shallow copy
ranges = RangeDict()
ranges._ranges = self._ranges.copy()
return ranges
def _slice(self, slice_):
assert isinstance(slice_, slice)
# create a copy
ranges = RangeDict()
ranges._ranges = self._ranges
# just use __delitem__ to do the carving, it's good enough probably
if slice_.stop is not None:
del ranges[range(slice_.stop, len(self))]
if slice_.start is not None:
del ranges[range(0, slice_.start)]
ranges._ranges = [(
range(
r.start - slice_.start,
r.stop - slice_.start),
v)
for r, v in ranges._ranges]
return ranges
def __ior__(self, other):
for r, v in other.ranges():
self[r] = v
return self
def __or__(self, other):
ranges = self.copy()
ranges |= other
return ranges
## a representation of a range of blocks/offs to show
#class BmapSlice:
# def __init__(self, blocks=None, off=None, size=None):
# self.orig_blocks = blocks
# self.orig_off = off
# self.orig_size = size
#
# # flatten blocks, default to all blocks
# blocks = ([blocks] if isinstance(blocks, slice)
# else list(it.chain.from_iterable(
# [blocks_] if isinstance(blocks_, slice) else blocks_
# for blocks_ in blocks))
# if blocks is not None
# else [slice(None)])
#
# # blocks may also encode offsets
# blocks, offs, size = (
# [block[0] if isinstance(block, tuple)
# else block
# for block in blocks],
# [off.start if isinstance(off, slice)
# else off if off is not None
# else size.start if isinstance(size, slice)
# else block[1] if isinstance(block, tuple)
# else None
# for block in blocks],
# (size.stop - (size.start or 0)
# if size.stop is not None
# else None) if isinstance(size, slice)
# else size if size is not None
# else ((off.stop - (off.start or 0))
# if off.stop is not None
# else None) if isinstance(off, slice)
# else None)
#
# self.blocks = blocks
# self.offs = offs
# self.size = size
#
# @property
# def off(self):
# if not self.offs:
# return None
# else:
# return self.offs[-1]
#
# def __repr__(self):
# return 'BmapSlice(%r, %r, %r)' % (
# self.orig_blocks,
# self.orig_off,
# self.orig_size)
#
# def slices(self, block_size, block_count):
# for block, off in zip(self.blocks, self.offs):
# # figure out off range, bound to block_size
# off = off if off is not None else 0
# size = self.size if self.size is not None else block_size - off
# if off >= block_size:
# continue
# size = min(off + size, block_size) - off
#
# # flatten/filter blocks
# if isinstance(block, slice):
# start = block.start if block.start is not None else 0
# stop = block.stop if block.stop is not None else block_count
# for block_ in range(start, min(stop, block_count)):
# yield block_, off, size
# else:
# block = block if block is not None else 0
# if block < block_count:
# yield block, off, size
#
# def blocks(self, block_count):
# for block in self.blocks:
# # flatten/filter blocks
# if isinstance(block, slice):
# start = block.start if block.start is not None else 0
# stop = block.stop if block.stop is not None else block_count
# for block_ in range(start, min(stop, block_count)):
# yield block_
# else:
# block = block if block is not None else 0
# if block < block_count:
# yield block
#
# def sorted_slices(self, block_size, block_count):
# # merge block ranges
# blocks = RangeDict()
# for block in self.blocks:
# # figure out off range, bound to block_size
# off = off if off is not None else 0
# size = self.size if self.size is not None else block_size - off
# if off >= block_size:
# continue
# size = min(off + size, block_size) - off
#
# # flatten/filter blocks
# if isinstance(block, slice):
# start = block.start if block.start is not None else 0
# stop = block.stop if block.stop is not None else block_count
# blocks.map(range(start, min(stop, block_count)),
# lambda r: (r if r is not None else RangeSet())
# | RangeSet([range(off, off+size)]))
# else:
# block = block if block is not None else 0
# if block < block_count:
# blocks.map(range(block, block+1),
# lambda r: (r if r is not None else RangeSet())
# | RangeSet([range(off, off+size)]))
#
# for block, r in blocks:
# for off, stop in r:
# yield off, stop-off
#
# def sorted_blocks(self, block_count):
# # merge block ranges
# blocks = RangeSet()
# for block in self.blocks:
# # flatten/filter blocks
# if isinstance(block, slice):
# start = block.start if block.start is not None else 0
# stop = block.stop if block.stop is not None else block_count
# blocks.add(range(start, min(stop, block_count)))
# else:
# block = block if block is not None else 0
# if block < block_count:
# blocks.add(range(block, block+1))
#
# yield from blocks
#
#
#
## # merge block ranges
## merged = []
## for blocks, offs in zip(self.blocks, self.offs):
## # simplify blocks first
## if not isinstance(blocks, slice):
## blocks = slice(blocks, blocks+1)
## blocks = slice(
## blocks.start if blocks.start is not None else 0,
## blocks.start if blocks.start is not None else block_count)
##
## # copy ranges before merge
## i = 0
## merged_ = []
## blocks_ = blocks
## while i < len(merged) and merged[i].stop < blocks_.start:
## merged_.append(merged[i])
##
## # merge ranges and append
## while i < len(merged) and merged[i].start <= blocks_.stop:
## blocks_ = slice(
## min(merged[i].start, blocks_.start),
## max(merged[i].stop, blocks_.stop))
## merged_.append(blocks_)
##
## # and copy the rest
## merged_.extend(merged[i:])
##
##
##
##
##
## for blocks_ in merged:
## # merge ranges?
## if blocks_
## else
#
#
# def __contains__(self, block):
# if isinstance(block, tuple):
# block, off = block
# else:
# block, off = block, None
#
# for block_, off_ in zip(self.blocks, self.offs):
# # in off range?
# if off is not None:
# off_ = off_ if off_ is not None else 0
# size_ = self.size
# if not (off >= off_
# and (size_ is None or off < off_ + size_)):
# continue
#
# # in block range?
# if isinstance(block_, slice):
# if ((block_.start is None or block >= block_.start)
# and (block_.stop is None or block < block_.stop)):
# return True
# else:
# if block_ is None or block == block_:
# return True
#
# return False
def main(disk, output, mroots=None, *,
trunk=None,
@@ -5799,47 +5223,6 @@ if __name__ == "__main__":
'--block-count',
type=lambda x: int(x, 0),
help="Block count in blocks.")
# # subparser for block arguments
# blocks_parser = argparse.ArgumentParser(
# prog="%s -@/--blocks" % parser.prog,
# allow_abbrev=False)
# blocks_parser.add_argument(
# 'blocks',
# nargs='*',
# type=lambda x: (
# slice(*(int(x, 0) if x.strip() else None
# for x in x.split(',', 1)))
# if ',' in x and '{' not in x
# else rbydaddr(x)),
# help="Block addresses, may be a range.")
# blocks_parser.add_argument(
# '--off',
# type=lambda x: (
# slice(*(int(x, 0) if x.strip() else None
# for x in x.split(',', 1)))
# if ',' in x
# else int(x, 0)),
# help="Show a specific offset, may be a range.")
# blocks_parser.add_argument(
# '-n', '--size',
# type=lambda x: (
# slice(*(int(x, 0) if x.strip() else None
# for x in x.split(',', 1)))
# if ',' in x
# else int(x, 0)),
# help="Show this many bytes, may be a range.")
#
# parser.add_argument(
# '-@', '--blocks',
# type=lambda blocks: BmapSlice(**{k: v
# for k, v in vars(blocks_parser.parse_intermixed_args(
# shlex.split(blocks))).items()
# if v is not None}),
# help="Optional blocks to show, may be a range. Can also include "
# "--off and -n/--size flags to indicate a range inside the "
# "block, both which may also be ranges.")
parser.add_argument(
'-@', '--blocks',
type=lambda x: (
-939
View File
@@ -53,13 +53,6 @@ WEAR_COLORS = ['1;30', '1;30', '1;30', '', '', '', '', '31', '31', '1;31']
# erases, so progs are more interesting
Z_ORDER = ['prog', 'erase', 'read', 'wear', 'noop']
# TODO rm
#CHARS = 'rpe-'
#COLORS = ['32', '35', '34', '90']
#
#WEAR_CHARS = '-123456789'
#WEAR_COLORS = ['90', '', '', '', '', '', '', '35', '35', '1;31']
CHARS_DOTS = " .':"
CHARS_BRAILLE = (
'⠀⢀⡀⣀⠠⢠⡠⣠⠄⢄⡄⣄⠤⢤⡤⣤' '⠐⢐⡐⣐⠰⢰⡰⣰⠔⢔⡔⣔⠴⢴⡴⣴'
@@ -917,473 +910,6 @@ class TraceBlock:
## space filling Hilbert-curve
##
## note we memoize the last curve since this is a bit expensive
##
#@ft.lru_cache(1)
#def hilbert_curve(width, height):
# # based on generalized Hilbert curves:
# # https://github.com/jakubcerveny/gilbert
# #
# def hilbert_(x, y, a_x, a_y, b_x, b_y):
# w = abs(a_x+a_y)
# h = abs(b_x+b_y)
# a_dx = -1 if a_x < 0 else +1 if a_x > 0 else 0
# a_dy = -1 if a_y < 0 else +1 if a_y > 0 else 0
# b_dx = -1 if b_x < 0 else +1 if b_x > 0 else 0
# b_dy = -1 if b_y < 0 else +1 if b_y > 0 else 0
#
# # trivial row
# if h == 1:
# for _ in range(w):
# yield (x,y)
# x, y = x+a_dx, y+a_dy
# return
#
# # trivial column
# if w == 1:
# for _ in range(h):
# yield (x,y)
# x, y = x+b_dx, y+b_dy
# return
#
# a_x_, a_y_ = a_x//2, a_y//2
# b_x_, b_y_ = b_x//2, b_y//2
# w_ = abs(a_x_+a_y_)
# h_ = abs(b_x_+b_y_)
#
# if 2*w > 3*h:
# # prefer even steps
# if w_ % 2 != 0 and w > 2:
# a_x_, a_y_ = a_x_+a_dx, a_y_+a_dy
#
# # split in two
# yield from hilbert_(x, y, a_x_, a_y_, b_x, b_y)
# yield from hilbert_(x+a_x_, y+a_y_, a_x-a_x_, a_y-a_y_, b_x, b_y)
# else:
# # prefer even steps
# if h_ % 2 != 0 and h > 2:
# b_x_, b_y_ = b_x_+b_dx, b_y_+b_dy
#
# # split in three
# yield from hilbert_(x, y, b_x_, b_y_, a_x_, a_y_)
# yield from hilbert_(x+b_x_, y+b_y_, a_x, a_y, b_x-b_x_, b_y-b_y_)
# yield from hilbert_(
# x+(a_x-a_dx)+(b_x_-b_dx), y+(a_y-a_dy)+(b_y_-b_dy),
# -b_x_, -b_y_, -(a_x-a_x_), -(a_y-a_y_))
#
# if width >= height:
# curve = hilbert_(0, 0, +width, 0, 0, +height)
# else:
# curve = hilbert_(0, 0, 0, +height, +width, 0)
#
# return list(curve)
#
## space filling Z-curve/Lebesgue-curve
##
## note we memoize the last curve since this is a bit expensive
##
#@ft.lru_cache(1)
#def lebesgue_curve(width, height):
# # we create a truncated Z-curve by simply filtering out the points
# # that are outside our region
# curve = []
# for i in range(2**(2*mt.ceil(mt.log2(max(width, height))))):
# # we just operate on binary strings here because it's easier
# b = '{:0{}b}'.format(i, 2*mt.ceil(mt.log2(i+1)/2))
# x = int(b[1::2], 2) if b[1::2] else 0
# y = int(b[0::2], 2) if b[0::2] else 0
# if x < width and y < height:
# curve.append((x, y))
#
# return curve
#
#
#class Pixel(int):
# __slots__ = ()
# def __new__(cls, state=0, *,
# wear=0,
# readed=False,
# proged=False,
# erased=False):
# return super().__new__(cls,
# state
# | (wear << 3)
# | (1 if readed else 0)
# | (2 if proged else 0)
# | (4 if erased else 0))
#
# @property
# def wear(self):
# return self >> 3
#
# @property
# def readed(self):
# return (self & 1) != 0
#
# @property
# def proged(self):
# return (self & 2) != 0
#
# @property
# def erased(self):
# return (self & 4) != 0
#
# def read(self):
# return Pixel(int(self) | 1)
#
# def prog(self):
# return Pixel(int(self) | 2)
#
# def erase(self):
# return Pixel((int(self) | 4) + 8)
#
# def clear(self):
# return Pixel(int(self) & ~7)
#
# def __or__(self, other):
# return Pixel(
# (int(self) | int(other)) & 7,
# wear=max(self.wear, other.wear))
#
# def worn(self, max_wear, *,
# block_cycles=None,
# wear_chars=None,
# **_):
# if wear_chars is None:
# wear_chars = WEAR_CHARS
#
# if block_cycles:
# return self.wear / block_cycles
# else:
# return self.wear / max(max_wear, len(wear_chars))
#
# def draw(self, max_wear, char=None, *,
# reads=True,
# progs=True,
# erases=True,
# wear=False,
# block_cycles=None,
# color=True,
# dots=False,
# braille=False,
# chars=None,
# wear_chars=None,
# colors=None,
# wear_colors=None,
# **_):
# # fallback to default chars/colors
# if chars is None:
# chars = CHARS
# if len(chars) < len(CHARS):
# chars = chars + CHARS[len(chars):]
#
# if colors is None:
# colors = COLORS
# if len(colors) < len(COLORS):
# colors = colors + COLORS[len(colors):]
#
# if wear_chars is None:
# wear_chars = WEAR_CHARS
#
# if wear_colors is None:
# wear_colors = WEAR_COLORS
#
# # compute char/color
# c = chars[3]
# f = [colors[3]]
#
# if wear:
# w = min(self.worn(
# max_wear,
# block_cycles=block_cycles,
# wear_chars=wear_chars),
# 1)
#
# c = wear_chars[int(w * (len(wear_chars)-1))]
# f.append(wear_colors[int(w * (len(wear_colors)-1))])
#
# if progs and self.proged:
# c = chars[1]
# f.append(colors[1])
# elif erases and self.erased:
# c = chars[2]
# f.append(colors[2])
# elif reads and self.readed:
# c = chars[0]
# f.append(colors[0])
#
# # override char?
# if char:
# c = char
#
# # apply colors
# if f and color:
# c = '%s%s\x1b[m' % (
# ''.join('\x1b[%sm' % f_ for f_ in f),
# c)
#
# return c
#
#
#class Bmap:
# def __init__(self, *,
# block_size=1,
# block_count=1,
# block_window=None,
# off_window=None,
# width=1,
# height=1,
# pixels=None):
# # default width to block_window or block_size
# if width is None:
# if block_window is not None:
# width = len(block_window)
# else:
# width = block_count
#
# # allocate pixels if not provided
# if pixels is None:
# pixels = [Pixel() for _ in range(width*height)]
#
# self.pixels = pixels
# self.block_size = block_size
# self.block_count = block_count
# self.block_window = block_window
# self.off_window = off_window
# self.width = width
# self.height = height
#
# @property
# def _block_window(self):
# if self.block_window is None:
# return range(0, self.block_count)
# else:
# return self.block_window
#
# @property
# def _off_window(self):
# if self.off_window is None:
# return range(0, self.block_size)
# else:
# return self.off_window
#
# @property
# def _window(self):
# return len(self._off_window)*len(self._block_window)
#
# def _op(self, f, block=None, off=None, size=None):
# if block is None:
# range_ = range(len(self.pixels))
# else:
# if off is None:
# off, size = 0, self.block_size
# elif size is None:
# off, size = 0, off
#
# # map into our window
# if block not in self._block_window:
# return
# block -= self._block_window.start
#
# size = (max(self._off_window.start,
# min(self._off_window.stop, off+size))
# - max(self._off_window.start,
# min(self._off_window.stop, off)))
# off = (max(self._off_window.start,
# min(self._off_window.stop, off))
# - self._off_window.start)
# if size == 0:
# return
#
# # map to our block space
# range_ = range(
# block*len(self._off_window) + off,
# block*len(self._off_window) + off+size)
# range_ = range(
# (range_.start*len(self.pixels)) // self._window,
# (range_.stop*len(self.pixels)) // self._window)
# range_ = range(
# range_.start,
# max(range_.stop, range_.start+1))
#
# # apply the op
# for i in range_:
# self.pixels[i] = f(self.pixels[i])
#
# def read(self, block=None, off=None, size=None):
# self._op(Pixel.read, block, off, size)
#
# def prog(self, block=None, off=None, size=None):
# self._op(Pixel.prog, block, off, size)
#
# def erase(self, block=None, off=None, size=None):
# self._op(Pixel.erase, block, off, size)
#
# def clear(self, block=None, off=None, size=None):
# self._op(Pixel.clear, block, off, size)
#
# def resize(self, *,
# block_size=None,
# block_count=None,
# width=None,
# height=None):
# block_size = (block_size if block_size is not None
# else self.block_size)
# block_count = (block_count if block_count is not None
# else self.block_count)
# width = width if width is not None else self.width
# height = height if height is not None else self.height
#
# if (block_size == self.block_size
# and block_count == self.block_count
# and width == self.width
# and height == self.height):
# return
#
# # transform our pixels
# self.block_size = block_size
# self.block_count = block_count
#
# pixels = []
# for x in range(width*height):
# # map into our old bd space
# range_ = range(
# (x*self._window) // (width*height),
# ((x+1)*self._window) // (width*height))
# range_ = range(
# range_.start,
# max(range_.stop, range_.start+1))
#
# # aggregate state
# pixels.append(ft.reduce(
# Pixel.__or__,
# self.pixels[range_.start:range_.stop],
# Pixel()))
#
# self.width = width
# self.height = height
# self.pixels = pixels
#
# def draw(self, row, *,
# reads=False,
# progs=False,
# erases=False,
# wear=False,
# hilbert=False,
# lebesgue=False,
# dots=False,
# braille=False,
# **args):
# # find max wear?
# max_wear = None
# if wear:
# max_wear = max(p.wear for p in self.pixels)
#
# # fold via a curve?
# if hilbert:
# grid = [None]*(self.width*self.height)
# for (x,y), p in zip(
# hilbert_curve(self.width, self.height),
# self.pixels):
# grid[x + y*self.width] = p
# elif lebesgue:
# grid = [None]*(self.width*self.height)
# for (x,y), p in zip(
# lebesgue_curve(self.width, self.height),
# self.pixels):
# grid[x + y*self.width] = p
# else:
# grid = self.pixels
#
# # need to wait for more trace output before rendering
# #
# # this is sort of a hack that knows the output is going to a terminal
# if (braille and self.height < 4) or (dots and self.height < 2):
# needed_height = 4 if braille else 2
#
# self.history = getattr(self, 'history', [])
# self.history.append(grid.copy())
#
# if len(self.history)*self.height < needed_height:
# # skip for now
# return None
#
# grid = list(it.chain.from_iterable(
# # did we resize?
# it.islice(it.chain(h, it.repeat(Pixel())),
# self.width*self.height)
# for h in self.history))
# self.history = []
#
# line = []
# if braille:
# # encode into a byte
# for x in range(0, self.width, 2):
# byte_p = 0
# best_p = Pixel()
# for i in range(2*4):
# p = grid[x+(2-1-(i%2)) + ((row*4)+(4-1-(i//2)))*self.width]
# best_p |= p
# if ((reads and p.readed)
# or (progs and p.proged)
# or (erases and p.erased)
# or (not reads and not progs and not erases
# and wear and p.worn(max_wear, **args) >= 0.7)):
# byte_p |= 1 << i
#
# line.append(best_p.draw(
# max_wear,
# CHARS_BRAILLE[byte_p],
# braille=True,
# reads=reads,
# progs=progs,
# erases=erases,
# wear=wear,
# **args))
# elif dots:
# # encode into a byte
# for x in range(self.width):
# byte_p = 0
# best_p = Pixel()
# for i in range(2):
# p = grid[x + ((row*2)+(2-1-i))*self.width]
# best_p |= p
# if ((reads and p.readed)
# or (progs and p.proged)
# or (erases and p.erased)
# or (not reads and not progs and not erases
# and wear and p.worn(max_wear, **args) >= 0.7)):
# byte_p |= 1 << i
#
# line.append(best_p.draw(
# max_wear,
# CHARS_DOTS[byte_p],
# dots=True,
# reads=reads,
# progs=progs,
# erases=erases,
# wear=wear,
# **args))
# else:
# for x in range(self.width):
# line.append(grid[x + row*self.width].draw(
# max_wear,
# reads=reads,
# progs=progs,
# erases=erases,
# wear=wear,
# **args))
#
# return ''.join(line)
def main(path='-', *,
block_size=None,
block_count=None,
@@ -1542,36 +1068,6 @@ def main(path='-', *,
## trace parser
# precompute trace regexes
# TODO rm me
# trace_pattern = re.compile(
# '^(?P<file>[^:]*):(?P<line>[0-9]+):trace:.*?bd_(?:'
# '(?P<create>create\w*)\('
# '(?:'
# 'block_size=(?P<block_size>\w+)'
# '|' 'block_count=(?P<block_count>\w+)'
# '|' '.*?' ')*'
# '\)'
# '|' '(?P<read>read)\('
# '\s*(?P<read_ctx>\w+)' '\s*,'
# '\s*(?P<read_block>\w+)' '\s*,'
# '\s*(?P<read_off>\w+)' '\s*,'
# '\s*(?P<read_buffer>\w+)' '\s*,'
# '\s*(?P<read_size>\w+)' '\s*\)'
# '|' '(?P<prog>prog)\('
# '\s*(?P<prog_ctx>\w+)' '\s*,'
# '\s*(?P<prog_block>\w+)' '\s*,'
# '\s*(?P<prog_off>\w+)' '\s*,'
# '\s*(?P<prog_buffer>\w+)' '\s*,'
# '\s*(?P<prog_size>\w+)' '\s*\)'
# '|' '(?P<erase>erase)\('
# '\s*(?P<erase_ctx>\w+)' '\s*,'
# '\s*(?P<erase_block>\w+)'
# '\s*\(\s*(?P<erase_size>\w+)\s*\)' '\s*\)'
# '|' '(?P<sync>sync)\('
# '\s*(?P<sync_ctx>\w+)' '\s*\)'
# ')\s*$')
init_pattern = re.compile(
'^(?P<file>[^ :]*):(?P<line>[0-9]+):trace:'
'.*?bd_createcfg\('
@@ -1879,8 +1375,6 @@ def main(path='-', *,
b.width = max(b.width, 1)
b.height = max(b.height, 1)
# TODO chars should probably take priority over braille/dots
# TODO limit these based on requested ops?
# assign chars based on op + block
for b in bmap.values():
b.chars = {}
@@ -1936,8 +1430,6 @@ def main(path='-', *,
# render to canvas in a specific z-order that prioritizes
# interesting ops
for op in reversed(Z_ORDER):
# TODO if we're rendering noops as dashes here, should we render
# unused as dashes even in usage mode in dbgbmap.py?
# don't render noops in braille/dots mode
if (braille or dots) and op == 'noop':
continue
@@ -2047,414 +1539,6 @@ def main(path='-', *,
b.clear()
# TODO rm me
#
#
#
#
#
#
#
#
#
# # exclusive wear or reads/progs/erases by default
# if not reads and not progs and not erases and not wear:
# reads = True
# progs = True
# erases = True
#
# # assume a reasonable lines/height if not specified
# #
# # note that we let height = None if neither hilbert or lebesgue
# # are specified, this is a bit special as the default may be less
# # than one character in height.
# if height is None and (hilbert or lebesgue):
# if lines is not None:
# height = lines
# else:
# height = 5
#
# if lines is None:
# if height is not None:
# lines = height
# else:
# lines = 5
#
# # is bd geometry specified?
# if isinstance(block_size, tuple):
# block_size, block_count_ = block_size
# if block_count is None:
# block_count = block_count_
#
# # try to simplify the block/off/size arguments a bit
# if not isinstance(block, dict):
# block = {'block': block}
# block, off, size = (
# block.get('block'),
# block.get('off'),
# block.get('size'))
#
# if not isinstance(block, tuple):
# block = block,
# if isinstance(off, tuple) and len(off) == 1:
# off, = off
# if isinstance(size, tuple) and len(size) == 1:
# if off is None:
# off, = size
# size = None
#
# if any(isinstance(b, tuple) and len(b) > 1 for b in block):
# print("error: more than one block address?",
# file=sys.stderr)
# sys.exit(-1)
# if isinstance(block[0], tuple):
# block = (block[0][0], *block[1:])
# if len(block) > 1 and isinstance(block[1], tuple):
# block = (block[0], block[1][0])
# if isinstance(block[0], tuple):
# block, off_ = (block[0][0], *block[1:]), block[0][1]
# if off is None:
# off = off_
# if len(block) > 1 and isinstance(block[1], tuple):
# block = (block[0], block[1][0])
# if len(block) == 1:
# block, = block
#
# if isinstance(off, tuple):
# off, size_ = off[0], off[1] - off[0]
# if size is None:
# size = size_
# if isinstance(size, tuple):
# off_, size = off[0], off[1] - off[0]
# if off is None:
# off = off_
#
# # is a block window specified?
# block_window = None
# if block is not None:
# if isinstance(block, tuple):
# block_window = range(*block)
# else:
# block_window = range(block, block+1)
#
# off_window = None
# if off is not None or size is not None:
# off_ = off if off is not None else 0
# size_ = size if size is not None else 1
# off_window = range(off_, off_+size_)
#
# # create our block device representation
# bmap = Bmap(
# block_size=block_size if block_size is not None else 1,
# block_count=block_count if block_count is not None else 1,
# block_window=block_window,
# off_window=off_window)
#
# def resize():
# nonlocal bmap
#
# # figure out best width/height
# if width is None:
# width_ = min(80, shutil.get_terminal_size((80, 5))[0])
# elif width:
# width_ = width
# else:
# width_ = shutil.get_terminal_size((80, 5))[0]
#
# if height is None:
# height_ = 0
# elif height:
# height_ = height
# else:
# height_ = shutil.get_terminal_size((80, 5))[1]
#
# # terminal size changed?
# if width_ != bmap.width or height_ != bmap.height:
# bmap.resize(
# # scale if we're printing with dots or braille
# width=2*width_ if braille else width_,
# height=max(
# 1,
# 4*height_ if braille
# else 2*height_ if dots
# else height_))
# resize()
#
# # keep track of some extra info
# readed = 0
# proged = 0
# erased = 0
#
# # parse a line of trace output
# pattern = re.compile(
# '^(?P<file>[^:]*):(?P<line>[0-9]+):trace:.*?bd_(?:'
# '(?P<create>create\w*)\('
# '(?:'
# 'block_size=(?P<block_size>\w+)'
# '|' 'block_count=(?P<block_count>\w+)'
# '|' '.*?' ')*'
# '\)'
# '|' '(?P<read>read)\('
# '\s*(?P<read_ctx>\w+)' '\s*,'
# '\s*(?P<read_block>\w+)' '\s*,'
# '\s*(?P<read_off>\w+)' '\s*,'
# '\s*(?P<read_buffer>\w+)' '\s*,'
# '\s*(?P<read_size>\w+)' '\s*\)'
# '|' '(?P<prog>prog)\('
# '\s*(?P<prog_ctx>\w+)' '\s*,'
# '\s*(?P<prog_block>\w+)' '\s*,'
# '\s*(?P<prog_off>\w+)' '\s*,'
# '\s*(?P<prog_buffer>\w+)' '\s*,'
# '\s*(?P<prog_size>\w+)' '\s*\)'
# '|' '(?P<erase>erase)\('
# '\s*(?P<erase_ctx>\w+)' '\s*,'
# '\s*(?P<erase_block>\w+)'
# '\s*\(\s*(?P<erase_size>\w+)\s*\)' '\s*\)'
# '|' '(?P<sync>sync)\('
# '\s*(?P<sync_ctx>\w+)' '\s*\)'
# ')\s*$')
# def parse(line):
# nonlocal bmap
# nonlocal readed
# nonlocal proged
# nonlocal erased
#
# # string searching is much faster than the regex here, and this
# # actually has a big impact given the sheer quantity of how much
# # trace output we have to deal with
# if 'trace' not in line or 'bd' not in line:
# return False
# m = pattern.match(line)
# if not m:
# return False
#
# if m.group('create'):
# # update our block size/count
# block_size_ = int(m.group('block_size'), 0)
# block_count_ = int(m.group('block_count'), 0)
#
# if reset:
# bmap = Bmap(
# block_size=block_size_,
# block_count=block_count_,
# block_window=bmap.block_window,
# off_window=bmap.off_window,
# width=bmap.width,
# height=bmap.height)
# elif ((block_size is None
# and block_size_ != bmap.block_size)
# or (block_count is None
# and block_count_ != bmap.block_count)):
# bmap.resize(
# block_size=block_size if block_size is not None
# else block_size_,
# block_count=block_count if block_count is not None
# else block_count_)
# return True
#
# elif m.group('read') and reads:
# block = int(m.group('read_block'), 0)
# off = int(m.group('read_off'), 0)
# size = int(m.group('read_size'), 0)
#
# if ((block_size is None and off+size > bmap.block_size)
# or (block_count is None and block >= bmap.block_count)):
# bmap.resize(
# block_size=block_size if block_size is not None
# else max(off+size, bmap.block_size),
# block_count=block_count if block_count is not None
# else max(block+1, bmap.block_count))
#
# bmap.read(block, off, size)
# readed += size
# return True
#
# elif m.group('prog') and progs:
# block = int(m.group('prog_block'), 0)
# off = int(m.group('prog_off'), 0)
# size = int(m.group('prog_size'), 0)
#
# if ((block_size is None and off+size > bmap.block_size)
# or (block_count is None and block >= bmap.block_count)):
# bmap.resize(
# block_size=block_size if block_size is not None
# else max(off+size, bmap.block_size),
# block_count=block_count if block_count is not None
# else max(block+1, bmap.block_count))
#
# bmap.prog(block, off, size)
# proged += size
# return True
#
# elif m.group('erase') and (erases or wear):
# block = int(m.group('erase_block'), 0)
# size = int(m.group('erase_size'), 0)
#
# if ((block_size is None and size > bmap.block_size)
# or (block_count is None and block >= bmap.block_count)):
# bmap.resize(
# block_size=block_size if block_size is not None
# else max(size, bmap.block_size),
# block_count=block_count if block_count is not None
# else max(block+1, bmap.block_count))
#
# bmap.erase(block, size)
# erased += size
# return True
#
# else:
# return False
#
# # print trace output
# def draw(f):
# nonlocal readed
# nonlocal proged
# nonlocal erased
#
# def writeln(s=''):
# f.write(s)
# f.write('\n')
# f.writeln = writeln
#
# # don't forget we've scaled this for braille/dots!
# for row in range(
# mt.ceil(bmap.height/4) if braille
# else mt.ceil(bmap.height/2) if dots
# else bmap.height):
# line = bmap.draw(row,
# reads=reads,
# progs=progs,
# erases=erases,
# wear=wear,
# block_cycles=block_cycles,
# color=color,
# dots=dots,
# braille=braille,
# hilbert=hilbert,
# lebesgue=lebesgue,
# **args)
# if line:
# f.writeln(line)
#
# # print some information about reads/progs/erases
# #
# # cat implies no-header, because a header wouldn't really make sense
# if not no_header and not cat:
# # compute total ops
# total = readed+proged+erased
#
# # compute stddev of wear using our bmap, this is a bit different
# # from reads/progs/erases which ignores any bmap window, but it's
# # what we have
# if wear:
# mean = (sum(p.wear for p in bmap.pixels)
# / max(len(bmap.pixels), 1))
# stddev = mt.sqrt(sum((p.wear - mean)**2 for p in bmap.pixels)
# / max(len(bmap.pixels), 1))
# worst = max((p.wear for p in bmap.pixels), default=0)
#
# # a bit of a hack here, but this forces our header to always be
# # at row zero
# if len(f.lines) == 0:
# f.lines.append('')
# f.lines[0] = 'bd %dx%d%s%s%s%s' % (
# bmap.block_size, bmap.block_count,
# ', %6s read' % ('%.1f%%' % (100*readed / max(total, 1)))
# if reads else '',
# ', %6s prog' % ('%.1f%%' % (100*proged / max(total, 1)))
# if progs else '',
# ', %6s erase' % ('%.1f%%' % (100*erased / max(total, 1)))
# if erases else '',
# ', %13s wear' % ('%.1fσ (%.1f%%)' % (
# worst / max(stddev, 1),
# 100*stddev / max(worst, 1)))
# if wear else '')
#
# bmap.clear()
# readed = 0
# proged = 0
# erased = 0
# resize()
#
#
# # read/parse/coalesce operations
# if cat:
# ring = sys.stdout
# else:
# ring = RingIO(lines + (1 if not no_header else 0), head)
#
# # if sleep print in background thread to avoid getting stuck in a read call
# event = th.Event()
# lock = th.Lock()
# if sleep:
# done = False
# def background():
# while not done:
# event.wait()
# event.clear()
# with lock:
# draw(ring)
# if not cat:
# ring.draw()
# # sleep a minimum amount of time to avoid flickering
# time.sleep(sleep or 0.01)
# th.Thread(target=background, daemon=True).start()
#
# try:
# while True:
# with openio(path) as f:
# changed = 0
# for line in f:
# with lock:
# changed += parse(line)
#
# # need to redraw?
# if changed and (not coalesce or changed >= coalesce):
# if sleep:
# event.set()
# else:
# draw(ring)
# if not cat:
# ring.draw()
# changed = 0
#
# if not keep_open:
# break
# # don't just flood open calls
# time.sleep(sleep or 2)
# except FileNotFoundError as e:
# print("error: file not found %r" % path,
# file=sys.stderr)
# sys.exit(-1)
# except KeyboardInterrupt:
# pass
#
# if sleep:
# done = True
# lock.acquire() # avoids https://bugs.python.org/issue42717
# if not cat:
# sys.stdout.write('\n')
#
#
#
#
#
#
#
# def trace_(line):
# # TODO
#
# # TODO adopt f -> ring name in all scripts?
# def draw_(ring, *,
# width=None,
# height=None):
# # TODO
## main loop
lock = th.Lock()
event = th.Event()
@@ -2489,15 +1573,12 @@ def main(path='-', *,
# keep track of history if lines specified
if lines is not None:
ring = RingIO(lines+1
# TODO expose no_header in other scripts for consistency
if not no_header and lines > 0
else lines)
def draw_():
# cat? write directly to stdout
if cat:
draw__(sys.stdout,
# TODO copy this pattern (width+height) for
# other scripts?
width=width,
# make space for shell prompt
height=-1 if height is ... else height)
@@ -2545,25 +1626,6 @@ def main(path='-', *,
sys.stdout.write('\n')
# # TODO WIP
#
# # cat? let main_ write directly to stdout
# else:
# main_(sys.stdout)
#
# # not cat? write to a bounded ring
# if not cat:
# ring = RingIO(lines, head=head)
# main_(ring)
# sys.stdout.write('\n')
if __name__ == "__main__":
import sys
import argparse
@@ -2759,7 +1821,6 @@ if __name__ == "__main__":
'--padding',
type=float,
help="Padding to add to each block. Defaults to 0.")
# TODO lines in dbgbmap.py?
parser.add_argument(
'-n', '--lines',
nargs='?',