Add library.scan_hierarchy to quickly gather layout stats

This commit is contained in:
jan 2021-03-26 08:24:13 -07:00
parent 34a334f6f3
commit fbe5cee7f5

View File

@ -1,16 +1,17 @@
""" """
File-level read/write functionality. File-level read/write functionality.
""" """
from typing import List, Dict, Tuple, Optional, BinaryIO, TypeVar, Type from typing import List, Dict, Tuple, Optional, BinaryIO, TypeVar, Type, MutableMapping
import io import io
from datetime import datetime from datetime import datetime
from dataclasses import dataclass from dataclasses import dataclass
from collections import defaultdict
from .basic import KlamathError from .basic import KlamathError
from .record import Record from .record import Record
from .records import HEADER, BGNLIB, ENDLIB, UNITS, LIBNAME from .records import HEADER, BGNLIB, ENDLIB, UNITS, LIBNAME
from .records import BGNSTR, STRNAME, ENDSTR from .records import BGNSTR, STRNAME, ENDSTR, SNAME, COLROW, ENDEL
from .records import BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF from .records import BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF
from .elements import Element, Reference, Text, Box, Boundary, Path, Node from .elements import Element, Reference, Text, Box, Boundary, Path, Node
@ -183,3 +184,50 @@ def read_elements(stream: BinaryIO) -> List[Element]:
stream.seek(size, io.SEEK_CUR) stream.seek(size, io.SEEK_CUR)
size, tag = Record.read_header(stream) size, tag = Record.read_header(stream)
return data return data
def scan_hierarchy(stream: BinaryIO) -> Dict[bytes, Dict[bytes, int]]:
"""
Scan through a GDS file, building a table of instance counts
`{b'structure_name': {b'ref_name': count}}`.
This is intended to provide a fast overview of the file's
contents without performing a full read of all elements.
Args:
stream: Seekable stream to read from. Should be positioned
before the first structure record, but possibly
already past the file header.
"""
structures = {}
ref_name = None
ref_count = None
cur_structure: MutableMapping[bytes, int] = defaultdict(int)
size, tag = Record.read_header(stream)
while tag != ENDLIB.tag:
if tag == BGNSTR.tag:
stream.seek(size, io.SEEK_CUR)
name = STRNAME.read(stream)
if name in structures:
raise KlamathError(f'Duplicate structure name: {name!r}')
cur_structure = defaultdict(int)
structures[name] = cur_structure
ref_name = None
ref_count = None
elif tag == SNAME.tag:
ref_name = SNAME.read_data(stream, size)
elif tag == COLROW.tag:
colrow = COLROW.read_data(stream, size)
ref_count = colrow[0] * colrow[1]
elif tag == ENDEL.tag:
if ref_count is None:
ref_count = 1
assert(ref_name is not None)
cur_structure[ref_name] += ref_count
else:
stream.seek(size, io.SEEK_CUR)
size, tag = Record.read_header(stream)
dict_structures = {key: dict(value) for key, value in structures.items()}
return dict_structures