Skip to content

Commit 2c4e429

Browse files
committed
Match mappings order with traceheader order
Note that we have no information that files like that do exist, but specification does not prevent them from existing and code seemed incomplete without this check.
1 parent 403aa4e commit 2c4e429

10 files changed

Lines changed: 92 additions & 24 deletions

File tree

lib/include/segyio/segy.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ int segy_samples( const char* binheader );
503503
*/
504504
int segy_traceheaders( const char* binheader, int* traceheader_count );
505505

506+
/* Reads into allocated memory traceheader names from first trace. */
507+
int segy_traceheader_names( segy_datasource* ds, char (*names)[8] );
508+
506509
/*
507510
* calculate delay recording time for the first trace.
508511
*/

lib/src/segy.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,32 @@ int segy_traceheaders(
18911891
return SEGY_OK;
18921892
}
18931893

1894+
int segy_traceheader_names(
1895+
segy_datasource* ds,
1896+
char ( *names )[8]
1897+
) {
1898+
memcpy( names[0], "SEG00000", 8 );
1899+
1900+
for( int i = 1; i < ds->metadata.traceheader_count; ++i ) {
1901+
int err = seek_traceheader_offset( ds, 0, i, 232 );
1902+
if( err != SEGY_OK ) return err;
1903+
1904+
err = ds->read( ds, names[i], 8 );
1905+
if( err != 0 ) return SEGY_DS_READ_ERROR;
1906+
1907+
// it is unclear how to interpret specification "May be ASCII or EBCDIC
1908+
// text." For proprietary headers we don't know the expected name, so
1909+
// have to assume that header name is encoded the same way as main text
1910+
// header. We however can guess the encoding in files that have only
1911+
// predefined headers to prevent 'open' from breaking.
1912+
bool is_ascii_seg_header = memcmp( names[i], "SEG", 3 ) == 0;
1913+
if( ds->metadata.encoding == SEGY_EBCDIC && !is_ascii_seg_header ) {
1914+
encode( names[i], names[i], e2a, 8 );
1915+
}
1916+
}
1917+
1918+
return SEGY_OK;
1919+
}
18941920

18951921
static int bswap_th(
18961922
const segy_datasource* ds,

lib/src/segy.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ segy_binheader_size
99
segy_binheader
1010
segy_write_binheader
1111
segy_samples
12+
segy_traceheader_names
1213
segy_delay_recoding_time
1314
segy_sample_interval
1415
segy_format

python/segyio/segyio.cpp

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <segyio/segy.h>
1414

1515
#include <algorithm>
16+
#include <array>
1617
#include <cstdint>
1718
#include <cstring>
1819
#include <memory>
@@ -689,33 +690,54 @@ PyObject* segyopen( segyfd* self, PyObject* args, PyObject* kwargs ) {
689690
self->stanzas.empty() ? 0 : self->stanzas.back().end_index();
690691

691692
err = segy_collect_metadata( ds, endianness, encoding, ext_textheader_count );
692-
if( err == SEGY_OK ) {
693-
self->format = ds->metadata.format;
694-
self->elemsize = ds->metadata.elemsize;
695-
self->trace0 = ds->metadata.trace0;
696-
self->samplecount = ds->metadata.samplecount;
697-
self->trace_bsize = ds->metadata.trace_bsize;
698-
self->traceheader_count = ds->metadata.traceheader_count;
699-
self->tracecount = ds->metadata.tracecount;
693+
if( err != SEGY_OK ) {
694+
std::ostringstream msg;
695+
msg << "unable to gather basic metadata from the file, error " << segy_errstr( err )
696+
<< ". Intermediate state:\n"
697+
<< " endianness=" << ds->metadata.endianness << "\n"
698+
<< " encoding=" << ds->metadata.encoding << "\n"
699+
<< " format=" << ds->metadata.format << "\n"
700+
<< " elemsize=" << ds->metadata.elemsize << "\n"
701+
<< " ext_textheader_count=" << ds->metadata.ext_textheader_count << "\n"
702+
<< " trace0=" << ds->metadata.trace0 << "\n"
703+
<< " samplecount=" << ds->metadata.samplecount << "\n"
704+
<< " trace_bsize=" << ds->metadata.trace_bsize << "\n"
705+
<< " traceheader_count=" << ds->metadata.traceheader_count << "\n"
706+
<< " tracecount=" << ds->metadata.tracecount;
707+
return RuntimeError( msg.str().c_str() );
708+
}
709+
710+
self->format = ds->metadata.format;
711+
self->elemsize = ds->metadata.elemsize;
712+
self->trace0 = ds->metadata.trace0;
713+
self->samplecount = ds->metadata.samplecount;
714+
self->trace_bsize = ds->metadata.trace_bsize;
715+
self->traceheader_count = ds->metadata.traceheader_count;
716+
self->tracecount = ds->metadata.tracecount;
700717

701-
Py_INCREF( self );
702-
return (PyObject*)self;
718+
719+
std::vector<std::array<char, 8>> traceheader_names(self->traceheader_count);
720+
err = segy_traceheader_names(ds, reinterpret_cast<char(*)[8]>(traceheader_names.data()));
721+
if( err ) return Error( err );
722+
723+
std::vector<segy_header_mapping> ordered_mappings;
724+
for (const auto& header_name : traceheader_names) {
725+
bool found = false;
726+
for (const auto& mapping : self->traceheader_mappings) {
727+
if (std::strncmp(mapping.name, header_name.data(), 8) == 0) {
728+
ordered_mappings.push_back(mapping);
729+
found = true;
730+
break;
731+
}
732+
}
733+
if (!found) {
734+
return KeyError("traceheader mapping for '%8.8s' not found", header_name.data());
735+
}
703736
}
737+
self->traceheader_mappings = std::move(ordered_mappings);
704738

705-
std::ostringstream msg;
706-
msg << "unable to gather basic metadata from the file, error " << segy_errstr( err )
707-
<< ". Intermediate state:\n"
708-
<< " endianness=" << ds->metadata.endianness << "\n"
709-
<< " encoding=" << ds->metadata.encoding << "\n"
710-
<< " format=" << ds->metadata.format << "\n"
711-
<< " elemsize=" << ds->metadata.elemsize << "\n"
712-
<< " ext_textheader_count=" << ds->metadata.ext_textheader_count << "\n"
713-
<< " trace0=" << ds->metadata.trace0 << "\n"
714-
<< " samplecount=" << ds->metadata.samplecount << "\n"
715-
<< " trace_bsize=" << ds->metadata.trace_bsize << "\n"
716-
<< " traceheader_count=" << ds->metadata.traceheader_count << "\n"
717-
<< " tracecount=" << ds->metadata.tracecount;
718-
return RuntimeError( msg.str().c_str() );
739+
Py_INCREF( self );
740+
return (PyObject*)self;
719741
}
720742

721743
PyObject* segycreate( segyfd* self, PyObject* args, PyObject* kwargs ) {

python/test/segy.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,6 +2430,18 @@ def test_open_with_custom_mapping():
24302430
with pytest.raises(ValueError, match=r"invalid field datatype*"):
24312431
segyio.open(testdata / 'mapping-unsupported-type.sgy')
24322432

2433+
with segyio.open(testdata / 'mapping-mixed-order.sgy') as f:
2434+
assert f.tracefield.names() == ["SEG00000", "SEG00001", "PRIVATE1", "PRIVATE2"]
2435+
2436+
with segyio.open(testdata / 'mapping-no-extension1.sgy') as f:
2437+
assert f.tracefield.names() == ["SEG00000", "PRIVATE1"]
2438+
2439+
with segyio.open(testdata / 'mapping-encoding-ext1.sgy') as f:
2440+
assert f.tracefield.names() == ["SEG00000", "SEG00001"]
2441+
2442+
with pytest.raises(KeyError, match=r"traceheader mapping for .* not found"):
2443+
segyio.open(testdata / 'mapping-encoding-private.sgy')
2444+
24332445

24342446
def test_tracefields():
24352447
with segyio.open(testdata / 'trace-header-extensions.sgy') as f:

test-data/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,15 @@ Files are created manually.
101101
| mapping-all-types-lsb.sgy | `flip-endianness.py mapping-all-types.sgy out.sgy` |
102102
| mapping-default.sgy | XML corresponds exactly to the default SEG-Y rev 2.1 mapping. |
103103
| mapping-empty.sgy | Root does not have minimal number of required fields. |
104+
| mapping-encoding-ext1.sgy | SEG00001 name encoding does not match text header encoding. |
105+
| mapping-encoding-private.sgy | Proprietary header name encoding != text header encoding. |
104106
| mapping-invalid-attribute.sgy | Last entry does not have 'byte' attribute. |
105107
| mapping-invalid-root.sgy | Root does not define standard header layout. |
106108
| mapping-invalid-value.sgy | Last standard header entry byte is out of range. |
107109
| mapping-minimal.sgy | File with minimal number of defined fields segyio can open. |
110+
| mapping-mixed-order.sgy | XML layouts are in different order than headers in file. |
108111
| mapping-multiple-stanzas.sgy | Other stanzas are present before and after layout stanza. |
112+
| mapping-no-extension1.sgy | Only standard and proprietary trace headers are present. |
109113
| mapping-shifted.sgy | Default mapping with all offsets shifted by 1 byte. |
110114
| mapping-unparsable.sgy | XML is unparsable. |
111115
| mapping-unsupported-type.sgy | One axis value is of type segyio doesn't allow in that field. |
7.61 KB
Binary file not shown.
8.08 KB
Binary file not shown.

test-data/mapping-mixed-order.sgy

8.55 KB
Binary file not shown.
7.61 KB
Binary file not shown.

0 commit comments

Comments
 (0)