Refactors conditional to polymorphism in HeaderPacker. About 5% faster at reading trace headers.

This commit is contained in:
Robert Smallshire
2015-05-06 11:31:12 +02:00
parent 002385fc84
commit 3f82865c2d
3 changed files with 50 additions and 24 deletions
+44 -18
View File
@@ -112,19 +112,28 @@ def compile_struct(header_format_class, start_offset=0, length_in_bytes=None, en
return cformat, field_name_allocations
def make_header_packer(header_format_class, endian='>'):
cformat, field_name_allocations = compile_struct(
header_format_class,
header_format_class.START_OFFSET_IN_BYTES,
header_format_class.LENGTH_IN_BYTES,
endian)
structure = Struct(cformat)
one_to_one = all(len(fields) == 1 for fields in field_name_allocations)
if one_to_one:
return BijectiveHeaderPacker(header_format_class, structure, field_name_allocations)
return SurjectiveHeaderPacker(header_format_class, structure, field_name_allocations)
class HeaderPacker:
"""Packing and unpacking header instances."""
def __init__(self, header_format_class, endian='>'):
def __init__(self, header_format_class, structure, field_name_allocations):
self._header_format_class = header_format_class
self._format, self._field_name_allocations = compile_struct(
header_format_class,
header_format_class.START_OFFSET_IN_BYTES,
header_format_class.LENGTH_IN_BYTES,
endian)
self._struct = Struct(self._format)
self._one_to_one = all(len(fields) == 1 for fields in self._field_name_allocations)
pass
self._structure = structure
self._field_name_allocations = field_name_allocations
@property
def header_format_class(self):
@@ -140,7 +149,16 @@ class HeaderPacker:
header.__class__.__name__
))
values = [getattr(header, names[0]) for names in self._field_name_allocations]
return self._struct.pack(*values)
return self._structure.pack(*values)
def __repr__(self):
return "{}({})".format(
self.__class__.__name__,
self._header_format_class.__name__)
class BijectiveHeaderPacker(HeaderPacker):
"""One-to-one packing/unpacking of serialised values to header fields."""
def unpack(self, buffer):
"""Unpack a header into a header object.
@@ -151,10 +169,23 @@ class HeaderPacker:
Returns:
The header object.
"""
values = self._struct.unpack(buffer)
values = self._structure.unpack(buffer)
return self._header_format_class(*values)
if self._one_to_one:
return self._header_format_class(*values)
class SurjectiveHeaderPacker(HeaderPacker):
"""One-to-many unpacking of serialised values to header fields."""
def unpack(self, buffer):
"""Unpack a header into a header object.
Overwrites any existing header field values with new values
obtained from the buffer.
Returns:
The header object.
"""
values = self._structure.unpack(buffer)
kwargs = {name: value
for names, value in zip(self._field_name_allocations, values)
@@ -162,11 +193,6 @@ class HeaderPacker:
return self._header_format_class(**kwargs)
def __repr__(self):
return "{}({})".format(
self.__class__.__name__,
self._header_format_class.__name__)
def main():
from segpy.trace_header import TraceHeaderRev0
+2 -2
View File
@@ -1,6 +1,6 @@
from __future__ import print_function
from segpy.encoding import ASCII
from segpy.packer import HeaderPacker
from segpy.packer import make_header_packer
from segpy.trace_header import TraceHeaderRev1
from segpy.util import file_length, filename_from_handle
@@ -175,7 +175,7 @@ class SegYReader(object):
self._binary_reel_header = binary_reel_header
self._extended_textual_headers = extended_textual_headers
self._trace_header_packer = HeaderPacker(trace_header_format, endian)
self._trace_header_packer = make_header_packer(trace_header_format, endian)
self._trace_offset_catalog = trace_offset_catalog
self._trace_length_catalog = trace_length_catalog
+4 -4
View File
@@ -15,7 +15,7 @@ from segpy.catalog import CatalogBuilder
from segpy.datatypes import SEG_Y_TYPE_TO_CTYPE, size_in_bytes, DATA_SAMPLE_FORMAT_TO_SEG_Y_TYPE, CTYPE_TO_SIZE
from segpy.encoding import guess_encoding, is_supported_encoding, UnsupportedEncodingError
from segpy.ibm_float import IBMFloat
from segpy.packer import HeaderPacker
from segpy.packer import make_header_packer
from segpy.revisions import canonicalize_revision
from segpy.trace_header import TraceHeaderRev1
from segpy.util import file_length, batched, pad, complementary_intervals, NATIVE_ENDIANNESS, EMPTY_BYTE_STRING
@@ -168,7 +168,7 @@ def read_binary_reel_header(fh, endian='>'):
endian: '>' for big-endian data (the standard and default), '<' for
little-endian (non-standard)
"""
header_packer = HeaderPacker(BinaryReelHeader, endian)
header_packer = make_header_packer(BinaryReelHeader, endian)
fh.seek(TEXTUAL_HEADER_NUM_BYTES) # Consider using from_one_based(BinaryReelHeader.START_OFFSET_IN_BYTES)
buffer = fh.read(BinaryReelHeader.LENGTH_IN_BYTES)
reel_header = header_packer.unpack(buffer)
@@ -336,7 +336,7 @@ def catalog_traces(fh, bps, trace_header_format=TraceHeaderRev1, endian='>', pro
if not callable(progress_callback):
raise TypeError("catalog_traces(): progress callback must be callable")
trace_header_packer = HeaderPacker(trace_header_format, endian)
trace_header_packer = make_header_packer(trace_header_format, endian)
length = file_length(fh)
@@ -637,7 +637,7 @@ def write_binary_reel_header(fh, binary_reel_header, endian='>'):
The file pointer for fh will be positioned at the first byte following
the binary reel header.
"""
header_packer = HeaderPacker(BinaryReelHeader, endian) # TODO: Hard wiring
header_packer = make_header_packer(BinaryReelHeader, endian) # TODO: Hard wiring
buffer = header_packer.pack(binary_reel_header)
fh.write(buffer)
fh.seek(REEL_HEADER_NUM_BYTES)