segyio-1.5.2/0000775000372000037200000000000013253720451012422 5ustar travistravissegyio-1.5.2/README.md0000664000372000037200000004603213253720451013706 0ustar travistravis# segyio # [![Travis](https://img.shields.io/travis/Statoil/segyio/master.svg?label=travis)](https://travis-ci.org/Statoil/segyio) [![Appveyor](https://ci.appveyor.com/api/projects/status/2i5cr8ui2t9qbxk9?svg=true)](https://ci.appveyor.com/project/statoil-travis/segyio) [![PyPI Updates](https://pyup.io/repos/github/Statoil/segyio/shield.svg)](https://pyup.io/repos/github/Statoil/segyio/) [![Python 3](https://pyup.io/repos/github/Statoil/segyio/python-3-shield.svg)](https://pyup.io/repos/github/Statoil/segyio/) ## Index ## * [Introduction](#introduction) * [Feature summary](#feature-summary) * [Getting started](#getting-started) * [Quick start](#quick-start) * [Get segyio](#get-segyio) * [Build segyio](#build-segyio) * [Tutorial](#tutorial) * [Basics](#basics) * [Modes](#modes) * [Mode examples](#mode-examples) * [Goals](#project-goals) * [Contributing](#contributing) * [Examples](#examples) * [Common issues](#common-issues) * [History](#history) ## Introduction ## Segyio is a small LGPL licensed C library for easy interaction with SEG-Y formatted seismic data, with language bindings for Python and Matlab. Segyio is an attempt to create an easy-to-use, embeddable, community-oriented library for seismic applications. Features are added as they are needed; suggestions and contributions of all kinds are very welcome. To catch up on the latest development and features, see the [changelog](changelog.md). To write future proof code, consult the planned [breaking changes](breaking-changes.md). ## Feature summary ## * A low-level C interface with few assumptions; easy to bind to other languages * Read and write binary and textual headers * Read and write traces and trace headers * Simple, powerful, and native-feeling Python interface with numpy integration * xarray integration with netcdf_segy * Some simple applications with unix philosophy ## Getting started ## When segyio is built and installed, you're ready to start programming! Check out the [tutorial](#tutorial), [examples](#examples), and the [example programs](python/examples). For a technical reference with examples and small recipes, start your favourite Python interpreter and type `help(segyio)` to get started - it is written with pydoc and should integrate well with IDLE, pycharm and other Python tools. ### Quick start ### ```python import segyio import numpy as np with segyio.open('file.sgy') as f: for trace in f.trace: filtered = trace[np.where(trace < 1e-2)] ``` See the [examples](#examples) for more. ### Get segyio ### A copy of segyio is available both as pre-built binaries and source code: * In Debian [unstable](https://packages.debian.org/source/sid/segyio) * `apt install python3-segyio` * Wheels for Python from [PyPI](https://pypi.python.org/pypi/segyio/) * `pip install segyio` * Source code from [github](https://github.com/statoil/segyio) * `git clone https://github.com/statoil/segyio` * Source code in [tarballs](https://github.com/Statoil/segyio/releases) ### Build segyio ### To build segyio you need: * A C99 compatible C compiler (tested mostly on gcc and clang) * A C++ compiler for the Python extension * [CMake](https://cmake.org/) version 2.8.12 or greater * [Python](https://www.python.org/) 2.7 or 3.x. * [numpy](http://www.numpy.org/) version 1.10 or greater * [setuptools](https://pypi.python.org/pypi/setuptools) version 28 or greater * [setuptools-scm](https://pypi.python.org/pypi/setuptools_scm) To build and install segyio, perform the following actions in your console: ```bash git clone https://github.com/Statoil/segyio mkdir segyio/build cd segyio/build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local make make install ``` `make install` must be done as root for a system install; if you want to install in your home directory, add `-DCMAKE_INSTALL_PREFIX=~/` or some other appropriate directory, or `make DESTDIR=~/ install`. Please ensure your environment picks up on non-standard install locations (PYTHONPATH, LD_LIBRARY_PATH and PATH). If you have multiple Python installations, or want to use some alternative interpreter, you can help cmake find the right one by passing `-DPYTHON_EXECUTABLE=/opt/python/binary` along with install prefix and build type. To build the matlab bindings, invoke CMake with the option `-DBUILD_MEX=ON`. In some environments the Matlab binaries are in a non-standard location, in which case you need to help CMake find the matlab binaries by passing `-DMATLAB_ROOT=/path/to/matlab`. #### Developers #### It's recommended to build in debug mode to get more warnings and to embed debug symbols in the objects. Substituting `Debug` for `Release` in the `CMAKE_BUILD_TYPE` is plenty. Tests are located in the language/tests directories, and it's highly recommended that new features added are demonstrated for correctness and contract by adding a test. All tests can be run by invoking `ctest`. Feel free to use the tests already written as a guide. After building segyio you can run the tests with `ctest`, executed from the build directory. Please note that to run the Python examples you need to let your environment know where to find the Python library. It can be installed as a user, or on adding the segyio/build/python library to your pythonpath. ## Tutorial ## All code in this tutorial assumes segyio is imported, and that numpy is available as np. ```python import segyio import numpy as np ``` This tutorial assumes you're familiar with Python and numpy. For a refresh, check out the [python tutorial](https://docs.python.org/3/tutorial/) and [numpy quickstart](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html) ### Basics ### Opening a file for reading is done with the `segyio.open` function, and idiomatically used with context managers. Using the `with` statement, files are properly closed even in the case of exceptions. By default, files are opened read-only. ```python with segyio.open(filename) as f: ... ``` Open accepts several options (for more a more comprehensive reference, check the open function's docstring with `help(segyio.open)`. The most important option is the second (optional) positional argument. To open a file for writing, do `segyio.open(filename, 'r+')`, from the C `fopen` function. Files can be opened in *unstructured* mode, either by passing `segyio.open` the optional arguments `strict=False`, in which case not establishing structure (inline numbers, crossline numbers etc.) is not an error, and `ignore_geometry=True`, in which case segyio won't even try to set these internal attributes. The segy file object has several public attributes describing this structure: * `f.ilines` Inferred inline numbers * `f.xlines` Inferred crossline numbers * `f.offsets` Inferred offsets numbers * `f.samples` Inferred sample offsets (frequency and recording time delay) * `f.unstructured` True if unstructured, False if structured * `f.ext_headers` The number of extended textual headers If the file is opened *unstructured*, all the line properties will will be `None`. ### Modes ### In segyio, data is retrived and written through so-called *modes*. Modes are abstract arrays, or addressing schemes, and change what names and indices mean. All modes are properties on the file handle object, support the `len` function, and reads and writes are done through `f.mode[]`. Writes are done with assignment. Modes support array slicing inspired by numpy. The following modes are available: * `trace` The trace mode offers raw addressing of traces as they are laid out in the file. This, along with `header`, is the only mode available for unstructured files. Traces are enumerated `0..len(f.trace)`. Reading a trace yields a numpy `ndarray`, and reading multiple traces yields a generator of `ndarray`s. Generator semantics are used and the same object is reused, so if you want to cache or address trace data later, you must explicitly copy. ```python >>> f.trace[10] >>> f.trace[-2] >>> f.trace[15:45] >>> f.trace[:45:3] ``` * `header` With addressing behaviour similar to `trace`, accessing items yield header objects instead of numpy `ndarray`s. Headers are dict like objects, where keys are integers, seismic unix-style keys (in segyio.su module) and segyio enums (segyio.TraceField). Header values can be updated by assigning a dict-like to it, and keys not present on the right-hand-side of the assignment are *unmodified*. ```python >>> f.header[5] = { segyio.su.tracl: 10 } >>> f.header[5].items() >>> f.header[5][25, 37] # read multiple values at once ``` * `iline`, `xline` These modes will raise an error if the file is unstructured. They consider arguments to `[]` as the *keys* of the respective lines. Line numbers are always increasing, but can have arbitrary, uneven spacing. The valid names can be found in the `ilines` and `xlines` properties. As with traces, getting one line yields an `ndarray`, and a slice of lines yields a generator of `ndarray`s. When using slices with a step, some intermediate items might be skipped if it is not matched by the step, i.e. doing `f.line[1:10:3]` on a file with lines `[1,2,3,4,5]` is equivalent of looking up `1, 4, 7`, and finding `[1,4]`. When working with a 4D pre-stack file, the first offset is implicitly read. To access a different or a range of offsets, use comma separated indices or ranges, as such: `f.iline[120, 4]`. * `fast`, `slow` These are aliases for `iline` and `xline`, determined by how the traces are laid out. For inline sorted files, `fast` would yield `iline`. * `depth_slice` The depth slice is a horizontal, file-wide cut at a depth. The yielded values are `ndarray`s and generators-of-arrays. * `gather` The `gather` is the intersection of an inline and crossline, a vertical column of the survey, and unless a single offset is specified returns an offset x samples `ndarray`. In the presence of ranges, it returns a generator of such `ndarray`s. * `text` The `text` mode is an array of the textual headers, where `text[0]` is the standard-mandated textual header, and `1..n` are the optional extended headers. The text headers are returned as 3200-byte string-like blobs (bytes in Python 3, str in Python 2), as it is in the file. The `segyio.tools.wrap` function can create a line-oriented version of this string. * `bin` The values of the file-wide binary header with a dict-like interface. Behaves like the `header` mode, but without the indexing. ### Mode examples ### ```python >>> for line in f.iline[:2430]: ... print(np.average(line)) >>> for line in f.xline[2:10]: ... print(line) >>> for line in f.fast[::2]: ... print(np.min(line)) >>> for factor, offset in enumerate(f.iline[10, :]): ... offset *= factor print(offset) >>> f.gather[200, 241, :].shape >>> text = f.text[0] >>> type(text) # 'str' in Python 2 >>> f.trace[10] = np.zeros(len(f.samples)) ``` More examples and recipes can be found in the docstrings `help(segyio)` and the [examples](#examples) section. ## Project goals ## Segyio does necessarily attempt to be the end-all of SEG-Y interactions; rather, we aim to lower the barrier to interacting with SEG-Y files for embedding, new applications or free-standing programs. Additionally, the aim is not to support the full standard or all exotic (but standard compliant) formatted files out there. Some assumptions are made, such as: * All traces in a file are assumed to be of the same size * All samples are 4-byte floats Currently, segyio supports: * Post-stack 3D volumes, sorted with respect to two header words (generally INLINE and CROSSLINE) * Pre-stack 4D volumes, sorted with respect to three header words (generally INLINE, CROSSLINE, and OFFSET) * Unstructured data The writing functionality in segyio is largely meant to *modify* or adapt files. A file created from scratch is not necessarily a to-spec SEG-Y file, as we only necessarily write the header fields segyio needs to make sense of the geometry. It is still highly recommended that SEG-Y files are maintained and written according to specification, but segyio does not enforce this. ## Contributing ## We welcome all kinds of contributions, including code, bug reports, issues, feature requests, and documentation. The preferred way of submitting a contribution is to either make an [issue](https://github.com/Statoil/segyio/issues) on github or by forking the project on github and making a pull request. ## xarray integration ## [Alan Richardson](https://github.com/ar4) has written a great little tool for using [xarray](http://xarray.pydata.org/en/stable/) with segy files, which he demos in this [notebook](https://github.com/ar4/netcdf_segy/blob/master/notebooks/netcdf_segy.ipynb) ## Reproducing the test data ## Small SEG-Y formatted files are included in the repository for test purposes. The data is non-sensical and made to be predictable, and it is reproducible by using segyio. The tests file are located in the test-data directory. To reproduce the data file, build segyio and run the test program `make-file.py`, `make-ps-file.py`, and `make-rotated-copies.py` as such: ```python python examples/make-file.py small.sgy 50 1 6 20 25 python examples/make-ps-file.py small-ps.sgy 10 1 5 1 4 1 3 python examples/make-rotated-copies.py small.sgy ``` If you have have small data files with a free license, feel free to submit it to the project! ## Examples ## ### Python ### Import useful libraries: ```python import segyio import numpy as np from shutil import copyfile ``` Open segy file and inspect it: ```python filename = 'name_of_your_file.sgy' with segyio.open(filename, "r") as segyfile: # Memory map file for faster reading (especially if file is big...) segyfile.mmap() # Print binary header info print(segyfile.bin) print(segyfile.bin[segyio.BinField.Traces]) # Read headerword inline for trace 10 print(segyfile.header[10][segyio.TraceField.INLINE_3D]) # Print inline and crossline axis print(segyfile.xlines) print(segyfile.ilines) ``` Read post-stack data cube contained in segy file: ```python # Read data along first xline data = segyfile.xline[segyfile.xlines[1]] # Read data along last iline data = segyfile.iline[segyfile.ilines[-1]] # Read data along 100th time slice data = segyfile.depth_slice[100] # Read data cube data = segyio.tools.cube(filename) ``` Read pre-stack data cube contained in segy file: ```python filename = 'name_of_your_prestack_file.sgy' with segyio.open(filename, "r") as segyfile: # Print offsets print(segyfile.offset) # Read data along first iline and offset 100: data [nxl x nt] data = segyfile.iline[0, 100] # Read data along first iline and all offsets gath: data [noff x nxl x nt] data = np.asarray([np.copy(x) for x in segyfile.iline[0:1, :]]) # Read data along first 5 ilines and all offsets gath: data [noff nil x nxl x nt] data = np.asarray([np.copy(x) for x in segyfile.iline[0:5, :]]) # Read data along first xline and all offsets gath: data [noff x nil x nt] data = np.asarray([np.copy(x) for x in segyfile.xline[0:1, :]]) ``` Read and understand fairly 'unstructured' data (e.g., data sorted in common shot gathers): ```python filename = 'name_of_your_prestack_file.sgy' with segyio.open(filename, "r", ignore_geometry=True) as segyfile: segyfile.mmap() # Extract header word for all traces sourceX = segyfile.attributes(segyio.TraceField.SourceX)[:] # Scatter plot sources and receivers color-coded on their number plt.figure() sourceY = segyfile.attributes(segyio.TraceField.SourceY)[:] nsum = segyfile.attributes(segyio.TraceField.NSummedTraces)[:] plt.scatter(sourceX, sourceY, c=nsum, edgecolor='none') groupX = segyfile.attributes(segyio.TraceField.GroupX)[:] groupY = segyfile.attributes(segyio.TraceField.GroupY)[:] nstack = segyfile.attributes(segyio.TraceField.NStackedTraces)[:] plt.scatter(groupX, groupY, c=nstack, edgecolor='none') ``` Write segy file using same header of another file but multiply data by *2 ```python input_file = 'name_of_your_input_file.sgy' output_file = 'name_of_your_output_file.sgy' copyfile(input_file, output_file) with segyio.open(output_file, "r+") as src: # multiply data by 2 for i in src.ilines: src.iline[i] = 2 * src.iline[i] ``` Make segy file from sctrach ```python spec = segyio.spec() filename = 'name_of_your_file.sgy' spec = segyio.spec() file_out = 'test1.sgy' spec.sorting = 2 spec.format = 1 spec.samples = np.arange(30) spec.ilines = np.arange(10) spec.xlines = np.arange(20) with segyio.create(filename, spec) as f: # write the line itself to the file and the inline number in all this line's headers for ilno in spec.ilines: f.iline[ilno] = np.zeros( (len(spec.xlines), spec.samples), dtype=np.single) + ilno f.header.iline[ilno] = { segyio.TraceField.INLINE_3D: ilno, segyio.TraceField.offset: 0 } # then do the same for xlines for xlno in spec.xlines: f.header.xline[xlno] = { segyio.TraceField.CROSSLINE_3D: xlno, segyio.TraceField.TRACE_SAMPLE_INTERVAL: 4000 } ``` Visualize data using sibling tool [SegyViewer](https://github.com/Statoil/segyviewer): ```python from PyQt4.QtGui import QApplication import segyviewlib qapp = QApplication([]) l = segyviewlib.segyviewwidget.SegyViewWidget('filename.sgy') l.show() ``` ### MATLAB ### ``` filename='name_of_your_file.sgy' % Inspect segy Segy_struct=SegySpec(filename,189,193,1); % Read headerword inline for each trace Segy.get_header(filename,'Inline3D') %Read data along first xline data= Segy.readCrossLine(Segy_struct,Segy_struct.crossline_indexes(1)); %Read cube data=Segy.get_cube(Segy_struct); %Write segy, use same header but multiply data by *2 input_file='input_file.sgy'; output_file='output_file.sgy'; copyfile(input_file,output_file) data = Segy.get_traces(input_file); data1 = 2*data; Segy.put_traces(output_file, data1); ``` ## Common issues ## ### ImportError: libsegyio.so.1: cannot open shared object file This error shows up when the loader cannot find the core segyio library. If you've explicitly set the install prefix (with `-DCMAKE_INSTALL_PREFIX`) you must configure your loader to also look in this prefix, either with a `ld.conf.d` file or the `LD_LIBRARY_PATH` variable. If you haven't set `CMAKE_INSTALL_PREFIX`, cmake will by default install to `/usr/local`, which your loader usually knows about. On Debian based systems, the library often gets installed to `/usr/local/lib`, which the loader may not know about. See [issue #239](https://github.com/Statoil/segyio/issues/239). #### Possible solutions * Configure the loader (`sudo ldconfig` often does the trick) * Install with a different, known prefix, e.g. `-DCMAKE_INSTALL_LIBDIR=lib64` ## History ## Segyio was initially written and is maintained by [Statoil ASA](http://www.statoil.com/) as a free, simple, easy-to-use way of interacting with seismic data that can be tailored to our needs, and as contribution to the free software community. segyio-1.5.2/man/0000775000372000037200000000000013253720451013175 5ustar travistravissegyio-1.5.2/man/segyio-crop.10000664000372000037200000000355313253720451015525 0ustar travistravis.TH SEGYIO-CROP 1 .SH NAME segyio-crop \- Copy a sub cube from SRC to DST .SH SYNPOSIS .B segyio-crop [\fIOPTION\fR]... \fISOURCE DEST\fR .SH DESCRIPTION .B segyio-crop Copy a sub cube from SEG-Y file SOURCE to DEST. .PP If no \fBbegin\fR or \fBend\fR options are specified, this program is essentially a copy. If a \fBbegin\fR option is omitted, the program copies from the start of the file. If an \fBend\fR option is omitted, the program copies until the end of file is reached. .PP Mandatory arguments to long options are mandatory for short options too. .SH OPTIONS .TP .BR \-i ", " \-\-iline-begin =\fILINE\fR inline to copy from .TP .BR \-I ", " \-\-iline-end=\fILINE\fR inline to copy to (inclusive) .TP .BR \-x ", " \-\-xline-begin =\fILINE\fR crossline to copy from .TP .BR \-X ", " \-\-xline-end=\fILINE\fR crossnline to copy to (inclusive) .TP .BR \-\-inline-begin =\fILINE\fR alias to \-\-iline-begin .TP .BR \-\-inline-end =\fILINE\fR alias to \-\-iline-end .TP .BR \-\-crossline-begin =\fILINE\fR alias to \-\-xline-begin .TP .BR \-\-crossline-end =\fILINE\fR alias to \-\-xline-end .TP .BR \-s ", " \-\-sample-begin =\fILINE\fR sample to copy from .TP .BR \-S ", " \-\-sample-end=\fILINE\fR sample to copy to (inclusive) .TP .BR \-b ", " \-\-il =\fINUM\fR inline header word byte offset; must align with SEG-Y defined offsets defaults to 189 .TP .BR \-B ", " \-\-xl =\fINUM\fR crossline header word byte offset; must align with SEG-Y defined offsets defaults to 193 .TP .BR \-v ", " \-\-verbose increase verbosity .TP .BR \-\-version output version information and exit .TP .BR \-\-help display this help and exit .SH COPYRIGHT Copyright © Statoil ASA. License LGPLv3+: GNU LGPL version 3 or later . .PP This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. segyio-1.5.2/man/segyio-catb.10000664000372000037200000000122213253720451015462 0ustar travistravis.TH SEGYIO-CATB 1 .SH NAME segyio-catb \- Concatenate SEG-Y binary header and print on seismic unix format .SH SYNPOSIS .B segyio-catb [\fIOPTION\fR]... [\fIFILE\fR]... .SH DESCRIPTION .B segyio-catb Concatenate the SEG-Y binary header in FILEs. .SH OPTIONS .TP .BR \-\-version output version information and exit .TP .BR \-\-help display this help and exit .SH COPYRIGHT Copyright © Statoil ASA. License LGPLv3+: GNU LGPL version 3 or later . .PP This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH SEE ALSO segyio-cath(1), segyio-catb(1) segyio-1.5.2/man/segyio-catr.10000664000372000037200000000273513253720451015514 0ustar travistravis.TH SEGYIO-CATR 1 .SH NAME segyio-catr \- Print selected headers from FILE .SH SYNPOSIS .B segyio-catr [\fIOPTION\fR]... \fIFILE\fR .SH DESCRIPTION .B segyio-cath Print selected traceheaders from SEG-Y file FILE .PP The flag\fB-r\fR takes up to three vales; START, STOP, STEP, which is a closed inteval from START to STOP. The third value STEP can be specified to print i.e. every other trace in the interval. All values are defaulted to zero. Flags \fB-r\fR and \fB-t\fR can be called multiple times. .PP Mandatory arguments to long options are mandatory for short options too. .SH OPTIONS .TP .BR \-t ", " \-\-trace=\fINUMBER\fR trace(s) to print .TP .BR \-r ", " \-\-range \fISTART\fR [\fSTOP\fR \fISTEP\fR] range of trace(s) to print. \fISTOP\fR and \fISTEP\fR are optional, and defaults to \fISTART\fR and 1 respectively. .TP .BR \-s ", " \-\-strict fail on unreadable tracefields .TP .BR \-S ", " \-\-non-strict don't fail on unreadable fields this is the default behavior .TP .BR \-n ", " \-\-segyio-names print with segyio tracefield names .TP .BR \-v ", " \-\-verbose increase verbosity .TP .BR \-\-version output version information and exit .TP .BR \-\-help display this help and exit .SH COPYRIGHT Copyright © Statoil ASA. License LGPLv3+: GNU LGPL version 3 or later . .PP This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH SEE ALSO segyio-cath(1), segyio-catb(1) segyio-1.5.2/man/CMakeLists.txt0000664000372000037200000000034513253720451015737 0ustar travistravisproject(segyio-man) if(NOT UNIX) return() endif() install(FILES segyio-cath.1 segyio-catb.1 segyio-catr.1 segyio-crop.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 ) segyio-1.5.2/man/segyio-cath.10000664000372000037200000000215113253720451015472 0ustar travistravis.TH SEGYIO-CATH 1 .SH NAME segyio-cath \- Concatenate SEG-Y text headers and print on the standard output .SH SYNPOSIS .B segyio-cath [\fIOPTION\fR]... [\fIFILE\fR]... .SH DESCRIPTION .B segyio-cath Concatenate the SEG-Y text headers in FILEs. .PP By default, only the non-extended header is printed, which is equivalent to --num 0. .PP Mandatory arguments to long options are mandatory for short options too. .SH OPTIONS .TP .BR \-n ", " \-\-num =\fINUM\fR the textual header to show, starts at 0 .TP .BR \-a ", " \-\-all print all headers .TP .BR \-s ", " \-\-strict abort if a header or file is not found primarily meant for shell scripts .TP .BR \-S ", " \-\-nonstrict ignore missing headers this is the default behaviour .TP .BR \-\-version output version information and exit .TP .BR \-\-help display this help and exit .SH COPYRIGHT Copyright © Statoil ASA. License LGPLv3+: GNU LGPL version 3 or later . .PP This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. .SH SEE ALSO segyio-catb(1), segyio-catr(1) segyio-1.5.2/setup.py0000664000372000037200000001060313253720451014134 0ustar travistravis#!/usr/bin/env python import os import sys from setuptools import setup, Extension long_description = """ ======= SEGY IO ======= Introduction ------------ Segyio is a small LGPL licensed C library for easy interaction with SEG Y formatted seismic data, with language bindings for Python and Matlab. Segyio is an attempt to create an easy-to-use, embeddable, community-oriented library for seismic applications. Features are added as they are needed; suggestions and contributions of all kinds are very welcome. Feature summary --------------- * A low-level C interface with few assumptions; easy to bind to other languages. * Read and write binary and textual headers. * Read and write traces, trace headers. * Easy to use and native-feeling python interface with numpy integration. Project goals ------------- Segyio does necessarily attempt to be the end-all of SEG-Y interactions; rather, we aim to lower the barrier to interacting with SEG-Y files for embedding, new applications or free-standing programs. Additionally, the aim is not to support the full standard or all exotic (but correctly) formatted files out there. Some assumptions are made, such as: * All traces in a file are assumed to be of the same sample size. * It is assumed all lines have the same number of traces. The writing functionality in Segyio is largely meant to *modify* or adapt files. A file created from scratch is not necessarily a to-spec SEG-Y file, as we only necessarily write the header fields segyio needs to make sense of the geometry. It is still highly recommended that SEG-Y files are maintained and written according to specification, but segyio does not mandate this. """ def src(x): root = os.path.dirname( __file__ ) return os.path.abspath(os.path.join(root, x)) extra_libs = ['m'] if not 'win' in sys.platform else [] def getversion(): # if this is a tarball distribution, the .git-directory won't be avilable # and setuptools_scm will crash hard. good tarballs are built with a # pre-generated version.py, so parse that and extract version from it # # set the SEGYIO_NO_GIT_VER environment variable to ignore a version from # git (useful when building for debian or other distributions) if not 'SEGYIO_NO_GIT_VER' in os.environ and os.path.isdir(src('.git')): return { 'use_scm_version': { 'root': src(''), 'write_to': src('python/segyio/version.py') } } pkgversion = { 'version': '0.0.0' } versionfile = src('python/segyio/version.py') if not os.path.exists(versionfile): return pkgversion import ast with open(versionfile) as f: root = ast.parse(f.read()) for node in ast.walk(root): if not isinstance(node, ast.Assign): continue if len(node.targets) == 1 and node.targets[0].id == 'version': pkgversion['version'] = node.value.s return pkgversion setup(name='segyio', description='Simple & fast IO for SEG-Y files', long_description=long_description, author='Statoil ASA', author_email='fg_gpl@statoil.com', url='https://github.com/Statoil/segyio', package_dir={'' : src('python')}, packages=['segyio'], package_data={ 'segyio': ['segyio.dll'], }, license='LGPL-3.0', ext_modules=[Extension('segyio._segyio', sources=[src('python/segyio/segyio.cpp')], include_dirs=[src('lib/include')], libraries=['segyio'] + extra_libs )], platforms='any', install_requires=['numpy >=1.10'], setup_requires=['setuptools >=28', 'setuptools_scm', 'pytest-runner'], tests_require=['pytest'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Other Environment', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)', 'Natural Language :: English', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities' ], **getversion() ) segyio-1.5.2/.gitmodules0000664000372000037200000000014313253720451014575 0ustar travistravis[submodule "multibuild"] path = multibuild url = https://github.com/matthew-brett/multibuild.git segyio-1.5.2/mex/0000775000372000037200000000000013253720451013213 5ustar travistravissegyio-1.5.2/mex/TraceField.m0000664000372000037200000000536613253720451015405 0ustar travistravisclassdef TraceField < int32 % add comment here enumeration TraceSequenceLine (1) TraceSequenceFile (5) FieldRecord (9) TraceNumber (13) EnergySourcePoint (17) cdp (21) cdpTrace (25) TraceIdentificationCode (29) NSummedTraces (31) NStackedTraces (33) DataUse (35) offset (37) ReceiverGroupElevation (41) SourceSurfaceElevation (45) SourceDepth (49) ReceiverDatumElevation (53) SourceDatumElevation (57) SourceWaterDepth (61) GroupWaterDepth (65) ElevationScalar (69) SourceGroupScalar (71) SourceX (73) SourceY (77) GroupX (81) GroupY (85) CoordinateUnits (89) WeatheringVelocity (91) SubWeatheringVelocity (93) SourceUpholeTime (95) GroupUpholeTime (97) SourceStaticCorrection (99) GroupStaticCorrection (101) TotalStaticApplied (103) LagTimeA (105) LagTimeB (107) DelayRecordingTime (109) MuteTimeStart (111) MuteTimeEND (113) ns (115) dt (117) GainType (119) InstrumentGainConstant (121) InstrumentInitialGain (123) Correlated (125) SweepFrequenceStart (127) SweepFrequenceEnd (129) SweepLength (131) SweepType (133) SweepTraceTaperLengthStart (135) SweepTraceTaperLengthEnd (137) TaperType (139) AliasFilterFrequency (141) AliasFilterSlope (143) NotchFilterFrequency (145) NotchFilterSlope (147) LowCutFrequency (149) HighCutFrequency (151) LowCutSlope (153) HighCutSlope (155) YearDataRecorded (157) DayOfYear (159) HourOfDay (161) MinuteOfHour (163) SecondOfMinute (165) TimeBaseCode (167) TraceWeightningFactor (169) GeophoneGroupNumberRoll1 (171) GeophoneGroupNumberFirstTraceOrigField (173) GeophoneGroupNumberLastTraceOrigField (175) GapSize (177) OverTravel (179) cdpX (181) cdpY (185) Inline3D (189) Crossline3D (193) ShotPoint (197) ShotPointScalar (201) TraceValueMeasurementUnit (203) TransductionConstantMantissa (205) TransductionConstantPower (209) TransductionUnit (211) TraceIdentifier (213) ScalarTraceHeader (215) SourceType (217) SourceEnergyDirectionMantissa (219) SourceEnergyDirectionExponent (223) SourceMeasurementMantissa (225) SourceMeasurementExponent (229) SourceMeasurementUnit (231) UnassignedInt1 (233) UnassignedInt2 (237) end end segyio-1.5.2/mex/SegySpec.m0000664000372000037200000000321113253720451015110 0ustar travistravisclassdef SegySpec % add comment here properties filename sample_format trace_sorting_format sample_indexes crossline_indexes inline_indexes offset_count offset first_trace_pos il_stride xl_stride trace_bsize t iline xline sorting end methods function obj = SegySpec(filename, inline_field, crossline_field, t0) dt = 4; spec = segyspec_mex(filename, int32(inline_field), int32(crossline_field), t0, dt); obj.filename = filename; if (isempty(spec)) e = MException('SegySpec:NoSuchFile', 'File %s not found',filename); throw(e); end obj.sample_format = uint32(SegySampleFormat(spec.sample_format)); obj.trace_sorting_format = TraceSortingFormat(spec.trace_sorting_format); obj.sample_indexes = spec.sample_indexes / 1000.0; obj.crossline_indexes = uint32(spec.crossline_indexes); obj.inline_indexes = uint32(spec.inline_indexes); obj.offset_count = uint32(spec.offset_count); obj.first_trace_pos = uint32(spec.first_trace_pos); obj.il_stride = spec.il_stride; obj.xl_stride = spec.xl_stride; obj.trace_bsize = spec.trace_bsize; obj.t = obj.sample_indexes; obj.iline = obj.inline_indexes; obj.xline = obj.crossline_indexes; [~, s] = enumeration(obj.trace_sorting_format); obj.sorting = s{uint32(obj.trace_sorting_format) + 1}; end end end segyio-1.5.2/mex/TraceSortingFormat.m0000664000372000037200000000021413253720451017143 0ustar travistravisclassdef TraceSortingFormat < int32 % add comment here enumeration unknown (0) xline (1) iline (2) end end segyio-1.5.2/mex/matlab.cmake0000664000372000037200000002015613253720451015461 0ustar travistravis# This module looks for mex, the MATLAB compiler. # The following variables are defined when the script completes: # MATLAB_MEX: location of mex compiler # MATLAB_ROOT: root of MATLAB installation # MATLABMEX_FOUND: 0 if not found, 1 if found SET(MATLABMEX_FOUND 0) SET(MATLABMCC_FOUND 0) IF(WIN32) # Win32 is Untested # Taken from the older FindMatlab.cmake script as well as # the modifications by Ramon Casero and Tom Doel for Gerardus. # Search for a version of Matlab available, starting from the most modern one # to older versions. FOREACH(MATVER "7.20" "7.19" "7.18" "7.17" "7.16" "7.15" "7.14" "7.13" "7.12" "7.11" "7.10" "7.9" "7.8" "7.7" "7.6" "7.5" "7.4") IF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "") OR ("${MATLAB_ROOT}" STREQUAL "/registry")) GET_FILENAME_COMPONENT(MATLAB_ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${MATVER};MATLABROOT]" ABSOLUTE) SET(MATLAB_VERSION ${MATVER}) ENDIF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "") OR ("${MATLAB_ROOT}" STREQUAL "/registry")) ENDFOREACH(MATVER) FIND_PROGRAM(MATLAB_MEX mex ${MATLAB_ROOT}/bin ) FIND_PROGRAM(MATLAB_MCC mex ${MATLAB_ROOT}/bin ) ELSE(WIN32) # Check if this is a Mac. IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Mac is untested # Taken from the older FindMatlab.cmake script as # well as the modifications by Ramon Casero and Tom Doel for Gerardus. SET(LIBRARY_EXTENSION .dylib) # If this is a Mac and the attempts to find MATLAB_ROOT have so far failed,~ # we look in the applications folder IF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "")) # Search for a version of Matlab available, starting from the most modern # one to older versions FOREACH(MATVER "2016b" "2014b" "R2013b" "R2013a" "R2012b" "R2012a" "R2011b" "R2011a" "R2010b" "R2010a" "R2009b" "R2009a" "R2008b") IF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "")) IF(EXISTS /Applications/MATLAB_${MATVER}.app) SET(MATLAB_ROOT /Applications/MATLAB_${MATVER}.app) ENDIF(EXISTS /Applications/MATLAB_${MATVER}.app) ENDIF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "")) ENDFOREACH(MATVER) ENDIF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL "")) FIND_PROGRAM(MATLAB_MEX mex PATHS ${MATLAB_ROOT}/bin ) FIND_PROGRAM(MATLAB_MCC mcc PATHS ${MATLAB_ROOT}/bin ) ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # On a Linux system. The goal is to find MATLAB_ROOT. SET(LIBRARY_EXTENSION .so) FIND_PROGRAM(MATLAB_MEX mex PATHS ${MATLAB_ROOT}/bin /prog/matlab/R2016B/bin # Statoil location /prog/matlab/R2014B/bin # Statoil location /opt/matlab/bin /usr/local/matlab/bin $ENV{HOME}/matlab/bin # Now all the versions /opt/matlab/[rR]20[0-9][0-9][abAB]/bin /usr/local/matlab/[rR]20[0-9][0-9][abAB]/bin /opt/matlab-[rR]20[0-9][0-9][abAB]/bin /opt/matlab_[rR]20[0-9][0-9][abAB]/bin /usr/local/matlab-[rR]20[0-9][0-9][abAB]/bin /usr/local/matlab_[rR]20[0-9][0-9][abAB]/bin $ENV{HOME}/matlab/[rR]20[0-9][0-9][abAB]/bin $ENV{HOME}/matlab-[rR]20[0-9][0-9][abAB]/bin $ENV{HOME}/matlab_[rR]20[0-9][0-9][abAB]/bin ) GET_FILENAME_COMPONENT(MATLAB_MEX "${MATLAB_MEX}" REALPATH) GET_FILENAME_COMPONENT(MATLAB_BIN_ROOT "${MATLAB_MEX}" PATH) # Strip ./bin/. GET_FILENAME_COMPONENT(MATLAB_ROOT "${MATLAB_BIN_ROOT}" PATH) FIND_PROGRAM(MATLAB_MCC mcc PATHS ${MATLAB_ROOT}/bin ) FIND_PROGRAM(MATLAB_MEXEXT mexext PATHS ${MATLAB_ROOT}/bin ) GET_FILENAME_COMPONENT(MATLAB_MCC "${MATLAB_MCC}" REALPATH) GET_FILENAME_COMPONENT(MATLAB_MEXEXT "${MATLAB_MEXEXT}" REALPATH) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ENDIF(WIN32) IF(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}") MESSAGE(FATAL_ERROR "Could not find MATLAB mex compiler; try specifying MATLAB_ROOT.") ELSE(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}") IF(EXISTS "${MATLAB_MEX}") MESSAGE(STATUS "Found MATLAB mex compiler: ${MATLAB_MEX}") MESSAGE(STATUS "MATLAB root: ${MATLAB_ROOT}") SET(MATLABMEX_FOUND 1) ENDIF(EXISTS "${MATLAB_MEX}") ENDIF(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}") IF(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}") MESSAGE(FATAL_ERROR "Could not find MATLAB mcc compiler; try specifying MATLAB_ROOT.") ELSE(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}") IF(EXISTS "${MATLAB_MCC}") MESSAGE(STATUS "Found MATLAB mcc compiler: ${MATLAB_MCC}") SET(MATLABMCC_FOUND 1) ENDIF(EXISTS "${MATLAB_MCC}") ENDIF(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}") MARK_AS_ADVANCED( MATLABMEX_FOUND ) SET(MATLAB_ROOT ${MATLAB_ROOT} CACHE FILEPATH "Path to a matlab installation") EXECUTE_PROCESS(COMMAND ${MATLAB_MEXEXT} OUTPUT_VARIABLE MEXEXT OUTPUT_STRIP_TRAILING_WHITESPACE) macro(mexo MEX_OBJECT) set(MEX_CFLAGS -fPIC -std=c99 -Werror) set(MEX_LDFLAGS) get_property(dirs DIRECTORY . PROPERTY INCLUDE_DIRECTORIES) foreach(dir ${dirs}) set(MEX_CFLAGS ${MEX_CFLAGS} -I${dir}) endforeach() set(MEX_LDFLAGS -shared) set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_OBJECT}.c) set(HEADER ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_OBJECT}.h) set(OBJECT ${CMAKE_CURRENT_BINARY_DIR}/${MEX_OBJECT}.o) add_custom_command(OUTPUT ${OBJECT} COMMAND ${MATLAB_MEX} -c CC="${CMAKE_C_COMPILER}" LD="${CMAKE_CXX_COMPILER}" CFLAGS="${MEX_CFLAGS}" LDFLAGS="${MEX_LDFLAGS}" -outdir ${CMAKE_CURRENT_BINARY_DIR} ${SOURCE} DEPENDS ${SOURCE} ${HEADER} ) add_custom_target(${MEX_OBJECT} ALL DEPENDS ${OBJECT}) endmacro() macro(mex MEX_NAME ) set(DEP ${ARG2}) set(MEX_CFLAGS -fPIC -std=c99 -Werror) set(MEX_LDFLAGS) get_property(dirs DIRECTORY . PROPERTY INCLUDE_DIRECTORIES) foreach(dir ${dirs}) set(MEX_CFLAGS ${MEX_CFLAGS} -I${dir}) endforeach() set(MEX_LDFLAGS -shared) set(MEX_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_NAME}.c) set(MEX_RESULT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MEX_NAME}.${MEXEXT}) add_custom_command(OUTPUT ${MEX_RESULT_FILE} COMMAND ${MATLAB_MEX} CC="${CMAKE_C_COMPILER}" CXX="${CMAKE_CXX_COMPILER}" LD="${CMAKE_CXX_COMPILER}" CFLAGS="${MEX_CFLAGS}" LDFLAGS="${MEX_LDFLAGS}" ${OBJECT} $ -outdir ${CMAKE_CURRENT_BINARY_DIR} ${MEX_SOURCE_FILE} DEPENDS ${MEX_SOURCE_FILE} segyio segyutil.o ) add_custom_target(${MEX_NAME} ALL DEPENDS ${MEX_RESULT_FILE} segyutil.o) set(${MEX_NAME}_TARGET ${MEX_NAME} PARENT_SCOPE) set(${MEX_NAME}_FILE ${MEX_RESULT_FILE} PARENT_SCOPE) endmacro() # this isn't meant to be run directly; use matlab_add_test or # matlab_add_example instead function(matlab_test TYPE TESTNAME MCC_SOURCE_FILE) configure_file(${MCC_SOURCE_FILE} ${MCC_SOURCE_FILE} COPYONLY) add_test(NAME ${TESTNAME} COMMAND ${MATLAB_ROOT}/bin/matlab -nodisplay -nosplash -nodesktop -r "addpath('../mex'), try, run('${MCC_SOURCE_FILE}'), exit(0), catch, exit(-1), end;" < /dev/null ) endfunction() function(add_matlab_test TESTNAME MCC_SOURCE_FILE) matlab_test(tests ${TESTNAME} ${MCC_SOURCE_FILE}) endfunction() # add_matlab_example takes an arbitrary number of arguments which it will # forward to the example program function(add_matlab_example TESTNAME MCC_SOURCE_FILE) matlab_test(examples ${TESTNAME} ${MCC_SOURCE_FILE}) endfunction() segyio-1.5.2/mex/segy_get_bfield_mex.c0000664000372000037200000000111313253720451017337 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { const char* bin = mxGetData( prhs[ 0 ] ); const int field = mxGetScalar( prhs[ 1 ] ); int f; int err = segy_get_bfield( bin, field, &f ); if( err == SEGY_INVALID_FIELD ) mexErrMsgIdAndTxt( "segy:get_bfield:invalid_field", "Invalid field value/header offset" ); plhs[ 0 ] = mxCreateDoubleScalar( f ); } segyio-1.5.2/mex/segy_put_headers_mex.c0000664000372000037200000000307113253720451017563 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1; char* msg2; int err; segy_file* fp = segyfopen( prhs[ 0 ], "r+b" ); double* headers = mxGetPr( prhs[ 1 ] ); int field = mxGetScalar( prhs[ 2 ] ); struct segy_file_format fmt = filefmt( fp ); char traceheader[ SEGY_TRACE_HEADER_SIZE ]; /* * check that the field is valid and writing it won't return an error. by * checking it here we don't have to do it in the write loop */ err = segy_set_field( traceheader, field, 0 ); if( err != 0 ) { msg1 = "segy:put_headers:invalid_field"; msg2 = "Invalid field value/header offset"; goto cleanup; } double* itr = headers; for( int i = 0; i < fmt.traces; ++i, ++itr ) { err = segy_traceheader( fp, i, traceheader, fmt.trace0, fmt.trace_bsize ); const int val = *itr; if( err != 0 ) { msg1 = "segy:put_headers:os"; msg2 = strerror( errno ); goto cleanup; } segy_set_field( traceheader, field, val ); err = segy_write_traceheader( fp, i, traceheader, fmt.trace0, fmt.trace_bsize ); if( err != 0 ) { msg1 = "segy:put_headers:os"; msg2 = strerror( errno ); goto cleanup; } } segy_close( fp ); return; cleanup: segy_close( fp ); mexErrMsgIdAndTxt( msg1, msg2 ); } segyio-1.5.2/mex/segyutil.h0000664000372000037200000000175413253720451015240 0ustar travistravis#ifndef SEGYIO_SEGYUTIL_H #define SEGYIO_SEGYUTIL_H #include #include #include #include typedef struct { char* filename; int sample_format; int* crossline_indexes; int crossline_count; int* inline_indexes; int inline_count; int offset_count; float* sample_indices; int sample_count; int trace_sorting_format; int il_stride; int xl_stride; long first_trace_pos; int trace_bsize; } SegySpec; int segyCreateSpec(SegySpec* spec, const char* file, unsigned int inline_field, unsigned int crossline_field, float t0, float dt); void recreateSpec(SegySpec* spec, const mxArray* mex_spec); struct segy_file_format { int samples; long trace0; int trace_bsize; int traces; int format; }; struct segy_file_format buffmt( const char* ); struct segy_file_format filefmt( segy_file* ); segy_file* segyfopen( const mxArray* filename, const char* mode ); #endif //SEGYIO_SEGYUTIL_H segyio-1.5.2/mex/segy_get_offsets_mex.c0000664000372000037200000000310513253720451017566 0ustar travistravis#include #include "mex.h" #include "matrix.h" #include "segyutil.h" #include void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { int errc = SEGY_OK; if (nrhs != 4) { goto ERROR; } const mxArray* mx_spec = prhs[0]; const mxArray* mx_offset = prhs[1]; const mxArray* mx_il_word = prhs[2]; const mxArray* mx_xl_word = prhs[3]; SegySpec spec; recreateSpec(&spec, mx_spec); int offset = (int)mxGetScalar(mx_offset); int il = (int)mxGetScalar(mx_il_word); int xl = (int)mxGetScalar(mx_xl_word); segy_file* fp = segy_open( spec.filename, "rb" ); if( !fp ) { errc = SEGY_FOPEN_ERROR; goto ERROR; }; plhs[0] = mxCreateNumericMatrix(1, spec.offset_count, mxINT32_CLASS, mxREAL); int* int_offsets = mxMalloc(sizeof( int ) * spec.offset_count); errc = segy_offset_indices(fp, offset, spec.offset_count, int_offsets, spec.first_trace_pos, spec.trace_bsize); if( errc != SEGY_OK ) goto CLEANUP; int32_t* plhs0 = (int32_t*)mxGetData(plhs[0]); for( int i = 0; i < spec.offset_count; ++i ) plhs0[i] = int_offsets[i]; mxFree( int_offsets ); segy_close(fp); return; CLEANUP: segy_close(fp); ERROR: { int nfields = 1; const char *fnames[nfields]; fnames[0] = "error"; plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames); mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(errc)); } } segyio-1.5.2/mex/segy_read_write_line_mex.c0000664000372000037200000000566413253720451020426 0ustar travistravis#include #include "mex.h" #include "matrix.h" #include "segyutil.h" #include /* The gateway function */ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { bool read; if (nrhs == 6) { read = true; } else if (nrhs == 7) { read = false; } else { goto ERROR; } const mxArray* mx_spec = prhs[0]; const mxArray* mx_index = prhs[1]; const mxArray* mx_line_length = prhs[2]; const mxArray* mx_line_indexes = prhs[3]; const mxArray* mx_stride = prhs[4]; const mxArray* mx_offsets = prhs[5]; SegySpec spec; recreateSpec(&spec,mx_spec); size_t index = (size_t)mxGetScalar(mx_index); uint32_t line_length = (uint32_t)mxGetScalar(mx_line_length); uint32_t* line_indexes = (uint32_t*)mxGetData(mx_line_indexes); int n = mxGetN(mx_line_indexes); int m = mxGetM(mx_line_indexes); uint32_t line_count = (n>m)? n:m; uint32_t stride = (uint32_t)mxGetScalar(mx_stride); int32_t offsets = (int32_t)mxGetScalar(mx_offsets); segy_file* fp; int line_trace0; int errc = segy_line_trace0( index, line_length, stride, offsets, line_indexes, line_count, &line_trace0 ); if (errc != 0) { goto CLEANUP; } if (read) { fp = segy_open( spec.filename, "rb" ); if (fp == NULL) { goto CLEANUP; } plhs[0] = mxCreateNumericMatrix(spec.sample_count, line_length, mxSINGLE_CLASS, mxREAL); float *data_ptr = (float *) mxGetData(plhs[0]); errc = segy_read_line( fp, line_trace0, line_length, stride, offsets, data_ptr, spec.first_trace_pos, spec.trace_bsize ); if (errc != 0) { goto CLEANUP; } errc = segy_to_native( spec.sample_format, line_length * spec.sample_count, data_ptr ); if (errc != 0) { goto CLEANUP; } } else { fp = segy_open( spec.filename, "r+b" ); if (fp == NULL) { goto CLEANUP; } const mxArray* mx_data = prhs[6]; float *data_ptr = (float *) mxGetData(mx_data); errc = segy_from_native( spec.sample_format, line_length * spec.sample_count, data_ptr ); if (errc != 0) { goto CLEANUP; } errc = segy_write_line( fp, line_trace0, line_length, stride, offsets, data_ptr, spec.first_trace_pos, spec.trace_bsize ); if (errc != 0) { goto CLEANUP; } errc = segy_to_native( spec.sample_format, line_length * spec.sample_count, data_ptr ); if (errc != 0) { goto CLEANUP; } } segy_close(fp); return; CLEANUP: segy_close(fp); ERROR: { int nfields = 1; const char *fnames[nfields]; fnames[0] = "error"; plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames); mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(errc)); } } segyio-1.5.2/mex/segy_get_traces_mex.c0000664000372000037200000000371213253720451017402 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1; char* msg2; int err; segy_file* fp = segyfopen( prhs[ 0 ], "rb" ); int first_trace = mxGetScalar( prhs[ 1 ] ); int last_trace = mxGetScalar( prhs[ 2 ] ); int notype = mxGetScalar( prhs[ 3 ] ); struct segy_file_format fmt = filefmt( fp ); char binary[ SEGY_BINARY_HEADER_SIZE ]; err = segy_binheader( fp, binary ); if( err != 0 ) { msg1 = "segy:get_traces:binary"; msg2 = strerror( errno ); goto cleanup; } // if last_trace was defaulted we assign it to the last trace in the file if( last_trace == -1 ) last_trace = fmt.traces - 1; int traces = 1 + (last_trace - first_trace); long long bufsize = (long long)fmt.samples * traces; plhs[0] = mxCreateNumericMatrix( fmt.samples, traces, mxSINGLE_CLASS, mxREAL ); float* out = mxGetData( plhs[ 0 ] ); if( first_trace > last_trace ) { msg1 = "segy:get_traces:bounds"; msg2 = "first trace must be smaller than last trace"; goto cleanup; } for( int i = first_trace; i <= last_trace; ++i ) { err = segy_readtrace( fp, i, out, fmt.trace0, fmt.trace_bsize ); out += fmt.samples; if( err != 0 ) { msg1 = "segy:get_traces:segy_readtrace"; msg2 = strerror( errno ); goto cleanup; } } segy_close( fp ); if( notype != -1 ) fmt.format = notype; segy_to_native( fmt.format, bufsize, mxGetData( plhs[ 0 ] ) ); int interval; segy_get_bfield( binary, SEGY_BIN_INTERVAL, &interval ); plhs[ 1 ] = mxCreateDoubleScalar( interval ); plhs[ 2 ] = mxCreateDoubleScalar( fmt.format ); return; cleanup: segy_close( fp ); mexErrMsgIdAndTxt( msg1, msg2 ); } segyio-1.5.2/mex/SegySampleFormat.m0000664000372000037200000000051013253720451016607 0ustar travistravisclassdef SegySampleFormat < int32 % add comment here enumeration IBM_FLOAT_4_BYTE (1) SIGNED_INTEGER_4_BYTE (2) SIGNED_SHORT_2_BYTE (3) FIXED_POINT_WITH_GAIN_4_BYTE (4) IEEE_FLOAT_4_BYTE (5) NOT_IN_USE_1 (6) NOT_IN_USE_2 (7) SIGNED_CHAR_1_BYTE (8) end end segyio-1.5.2/mex/segy_get_ps_line_mex.c0000664000372000037200000000304213253720451017546 0ustar travistravis#include #include "mex.h" #include "matrix.h" #include "segyutil.h" #include void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { int errc = 0; if (nrhs != 4) { goto ERROR; } const mxArray* mx_spec = prhs[0]; const mxArray* mx_offset = prhs[1]; const mxArray* mx_il_word = prhs[2]; const mxArray* mx_xl_word = prhs[3]; SegySpec spec; recreateSpec(&spec, mx_spec); int offset = (int)mxGetScalar(mx_offset); int il = (int)mxGetScalar(mx_il_word); int xl = (int)mxGetScalar(mx_xl_word); segy_file* fp = segyio_open( spec.filename, "rb" ); if( !fp ) { errc = SEGY_FOPEN_ERROR; goto ERROR; }; plhs[0] = mxCreateNumericMatrix(spec.sample_count, spec.offset_count, mxINT32_CLASS, mxREAL); int* int_offsets = mxMalloc(sizeof( int ) * spec.offset_count); errc = segy_offsets(fp, offset, spec.offset_count, int_offsets, spec.first_trace_pos, spec.trace_bsize); if( err != SEGY_OK ) goto CLEANUP; int32_t* plhs0 = (int32_t*)mxGetScalar(plhs[0]); for( int i = 0; i < spec.sample_count; ++i ) plhs0[i] = int_offsets[i]; segy_close(fp); return; CLEANUP: segy_close(fp); ERROR: { int nfields = 1; const char *fnames[nfields]; fnames[0] = "error"; plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames); mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(errc)); } } segyio-1.5.2/mex/segy_get_field_mex.c0000664000372000037200000000111113253720451017173 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { const char* bin = mxGetData( prhs[ 0 ] ); const int field = mxGetScalar( prhs[ 1 ] ); int f; int err = segy_get_field( bin, field, &f ); if( err == SEGY_INVALID_FIELD ) mexErrMsgIdAndTxt( "segy:get_field:invalid_field", "Invalid field value/header offset" ); plhs[ 0 ] = mxCreateDoubleScalar( f ); } segyio-1.5.2/mex/test/0000775000372000037200000000000013253720451014172 5ustar travistravissegyio-1.5.2/mex/test/segyspec.m0000664000372000037200000000461313253720451016176 0ustar travistravis% test segyspec % preconditions filename = 'test-data/small.sgy'; assert(exist(filename,'file')==2); t0 = 1111.0; %% no such file no_such_filename = 'no-such-dir/no-such-file.sgy'; assert(exist(no_such_filename,'file')~=2); try spec = SegySpec(no_such_filename, TraceField.Inline3D, TraceField.Crossline3D, t0); %should not reach here assert(false); catch %not actually needed... assert(true); end %% Spec is created try spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); catch %nothing should be caught assert(false); end %% IBM_FLOAT_4_BYTE spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(spec.sample_format == SegySampleFormat.IBM_FLOAT_4_BYTE); %% filename is set spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(strcmp(spec.filename,filename)); %% trace_sorting_format spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(spec.trace_sorting_format == TraceSortingFormat.iline); %%offset_count spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(length(spec.offset_count) == 1); %% sample_indexes spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); sample_indexes = spec.sample_indexes; assert(length(sample_indexes) == 50); for i = 1:length(sample_indexes) t = t0 + (i-1) * 4; assert(sample_indexes(i) == t); end %% first_trace_pos spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); first_trace_pos = spec.first_trace_pos; assert(first_trace_pos == 3600); %% il_stride spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); il_stride = spec.il_stride; assert(il_stride == 1); %% xl_stride spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); xl_stride = spec.xl_stride; assert(xl_stride == 5); %% xl_stride spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); trace_bsize = spec.trace_bsize; assert(trace_bsize == 50*4); %% xline spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(length(spec.crossline_indexes)==5) for xl = spec.crossline_indexes' assert(xl >= 20 && xl <= 24); end %% iline spec = SegySpec(filename, TraceField.Inline3D, TraceField.Crossline3D, t0); assert(length(spec.inline_indexes)==5) for il = spec.inline_indexes' assert(il >= 1 && il <= 5); end segyio-1.5.2/mex/test/segy.m0000664000372000037200000001737313253720451015332 0ustar travistravis% test segyline % preconditions filename = 'test-data/small.sgy'; assert(exist(filename,'file')==2); t0 = 1111.0; %% Spec is created try spec = Segy.interpret_segycube(filename, 'Inline3D', 'Crossline3D', t0); spec = Segy.interpret_segycube(filename, TraceField.Inline3D, 'Crossline3D', t0); spec = Segy.interpret_segycube(filename, TraceField.Inline3D, 193, t0); data = Segy.get_cube( spec ); assert(all(size(data) == [50, 5, 5])); catch %nothing should be caught assert(false); end % fail when file doesn't exist try Segy.get_header('does-not-exist', 193); assert(false); catch assert(true); end try Segy.get_traces('does-not-exist', 189, 193 ); assert(false); catch assert(true); end try Segy.get_ntraces('does-not-exist'); assert(false); catch assert(true); end try Segy.interpret_segycube('does-not-exist'); assert(false); catch assert(true); end %% Segy.readInLine 4 spec = Segy.interpret_segycube(filename, 'Inline3D', 'Crossline3D', t0); data = Segy.get_line(spec, 'iline', 4); sample_count = length(spec.sample_indexes); eps = 1e-6; % first trace along xline % first sample assert(abs(data(1, 1) - 4.2) #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1; char* msg2; int err; segy_file* fp = segyfopen( prhs[ 0 ], "rb" ); char* textheader = mxMalloc( segy_textheader_size() ); err = segy_read_textheader( fp, textheader ); if( err != 0 ) { msg1 = "segy:text_header:os"; msg2 = strerror( errno ); goto cleanup; } plhs[ 0 ] = mxCreateString( textheader ); mwSize dims[ 1 ] = { segy_binheader_size() }; plhs[ 1 ] = mxCreateCharArray( 1, dims ); err = segy_binheader( fp, mxGetData( plhs[ 1 ] ) ); if( err != 0 ) { msg1 = "segy:binary_header:os"; msg2 = strerror( errno ); goto cleanup; } mxFree( textheader ); return; cleanup: segy_close( fp ); mexErrMsgIdAndTxt( msg1, msg2 ); } segyio-1.5.2/mex/CMakeLists.txt0000664000372000037200000000453713253720451015764 0ustar travistravisproject(segyio-mex) if (NOT BUILD_MEX) unset(MATLAB_MCC CACHE) unset(MATLAB_MEX CACHE) unset(MATLAB_MEXEXT CACHE) unset(MATLAB_ROOT CACHE) unset(BUILD_MEX_TESTS CACHE) return() endif() include(matlab.cmake REQUIRED) option(BUILD_MEX_TESTS "Build matlab mex tests" ON) configure_file(Segy.m Segy.m) configure_file(SegySpec.m SegySpec.m) configure_file(SegySampleFormat.m SegySampleFormat.m) configure_file(TraceSortingFormat.m TraceSortingFormat.m) configure_file(TraceField.m TraceField.m) get_property(dirs TARGET segyio-shared PROPERTY INCLUDE_DIRECTORIES) include_directories(${dirs}) mexo(segyutil) mex(segyspec_mex) mex(segy_read_write_line_mex segyutil) mex(segy_read_write_ps_line_mex segyutil) mex(segy_get_header_mex segyutil) mex(segy_get_traces_mex segyutil) mex(segy_put_traces_mex segyutil) mex(segy_get_ntraces_mex segyutil) mex(segy_get_segy_header_mex segyutil) mex(segy_get_bfield_mex segyutil) mex(segy_get_trace_header_mex segyutil) mex(segy_get_field_mex segyutil) mex(segy_put_headers_mex segyutil) mex(segy_get_offsets_mex segyutil) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/segyspec_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_read_write_line_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_read_write_ps_line_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_header_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_traces_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_put_traces_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_ntraces_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_segy_header_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_bfield_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_trace_header_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_field_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_put_headers_mex.mexa64 ${CMAKE_CURRENT_BINARY_DIR}/segy_get_offsets_mex.mexa64 SegySpec.m Segy.m SegySampleFormat.m TraceSortingFormat.m TraceField.m DESTINATION ${CMAKE_INSTALL_PREFIX}/matlab) if(BUILD_MEX_TESTS) configure_file(${testdata}/small.sgy test/test-data/small.sgy COPYONLY) configure_file(${testdata}/small-ps.sgy test/test-data/small-ps.sgy COPYONLY) add_matlab_test(matlab.spec test/segyspec.m) add_matlab_test(matlab.segy test/segy.m) endif() segyio-1.5.2/mex/segy_get_ntraces_mex.c0000664000372000037200000000052113253720451017553 0ustar travistravis#include #include "segyutil.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { segy_file* fp = segyfopen( prhs[ 0 ], "rb" ); struct segy_file_format fmt = filefmt( fp ); segy_close( fp ); plhs[0] = mxCreateDoubleScalar( fmt.traces ); } segyio-1.5.2/mex/segy_read_write_ps_line_mex.c0000664000372000037200000000752713253720451021130 0ustar travistravis#include #include "mex.h" #include "matrix.h" #include "segyutil.h" #include /* The gateway function */ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if( nrhs < 5 || nrhs > 6 ) goto ERROR; bool read = nrhs == 5; const mxArray* mx_spec = prhs[0]; const mxArray* mx_index = prhs[1]; const mxArray* mx_line_length = prhs[2]; const mxArray* mx_line_indexes = prhs[3]; const mxArray* mx_stride = prhs[4]; SegySpec spec; recreateSpec(&spec, mx_spec); size_t index = (size_t)mxGetScalar(mx_index); uint32_t line_length = (uint32_t)mxGetScalar(mx_line_length); uint32_t* line_indexes = (uint32_t*)mxGetData(mx_line_indexes); int n = mxGetN(mx_line_indexes); int m = mxGetM(mx_line_indexes); uint32_t line_count = (n>m)? n:m; uint32_t stride = (uint32_t)mxGetScalar(mx_stride); int offsets = spec.offset_count; segy_file* fp; int line_trace0; int errc = segy_line_trace0( index, line_length, stride, offsets, line_indexes, line_count, &line_trace0 ); if( errc != SEGY_OK ) { mexErrMsgIdAndTxt( "segy:get_ps_line:wrong_line_number", "Specified line number is not in cube." ); return; } const char* mode = read ? "rb" : "r+b"; fp = segy_open( spec.filename, mode ); if( !fp ) { mexErrMsgIdAndTxt( "segy:get:ps_line:file", "unable to open file" ); return; } mwSize dims[] = { spec.sample_count, spec.offset_count, line_length }; const size_t tr_size = SEGY_TRACE_HEADER_SIZE + spec.trace_bsize; if( read ) { plhs[0] = mxCreateNumericArray(3, dims, mxSINGLE_CLASS, mxREAL ); float* buf = (float*) mxGetData(plhs[0]); for( int i = 0; i < offsets; ++i ) { errc = segy_read_line( fp, line_trace0, line_length, stride, offsets, buf + (spec.sample_count * line_length * i), spec.first_trace_pos + (i * tr_size), spec.trace_bsize ); if( errc != 0 ) goto CLEANUP; } errc = segy_to_native( spec.sample_format, offsets * line_length * spec.sample_count, buf ); if( errc != 0 ) goto CLEANUP; } else { const mxArray* mx_data = prhs[5]; float* buf = (float*) mxGetData(mx_data); errc = segy_from_native( spec.sample_format, offsets * line_length * spec.sample_count, buf ); if( errc != 0 ) goto CLEANUP; for( int i = 0; i < offsets; ++i ) { errc = segy_write_line( fp, line_trace0, line_length, stride, offsets, buf + (spec.sample_count * line_length * i), spec.first_trace_pos + (i * tr_size), spec.trace_bsize ); if( errc != 0 ) goto CLEANUP; } errc = segy_to_native( spec.sample_format, offsets * line_length * spec.sample_count, buf ); if( errc != 0 ) goto CLEANUP; } segy_close(fp); return; CLEANUP: segy_close(fp); ERROR: { int nfields = 1; const char *fnames[nfields]; fnames[0] = "error"; plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames); mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(errc)); } } segyio-1.5.2/mex/Segy.m0000664000372000037200000007265013253720451014312 0ustar travistravisclassdef Segy % add comment here properties spec end methods(Static) function obj = readInLine(spec, index) obj = segy_read_write_line_mex(spec, index, max(size(spec.crossline_indexes)), spec.inline_indexes, spec.il_stride, spec.offset_count); end function obj = readCrossLine(spec, index) obj = segy_read_write_line_mex(spec, index, max(size(spec.inline_indexes)), spec.crossline_indexes, spec.xl_stride, spec.offset_count); end function obj = writeCrossLine(spec, data, index) segy_read_write_line_mex(spec, index, max(size(spec.inline_indexes)), spec.crossline_indexes, spec.xl_stride, spec.offset_count, data); obj = data; end function obj = writeInLine(spec, data, index) segy_read_write_line_mex(spec, index, max(size(spec.crossline_indexes)), spec.inline_indexes, spec.il_stride, spec.offset_count, data); obj = data; end function data = get_line(cube, dir, n) if strcmpi(dir, 'iline') data = segy_read_write_line_mex(cube, n, max(size(cube.crossline_indexes)), cube.inline_indexes, cube.il_stride, cube.offset_count); elseif strcmpi(dir, 'xline') data = segy_read_write_line_mex(cube, n, max(size(cube.inline_indexes)), cube.crossline_indexes, cube.xl_stride, cube.offset_count); else error('Only iline and xline are valid directions.'); end end function data = put_line(cube, data, dir, n) if strcmpi(dir, 'iline') segy_read_write_line_mex(cube, n, max(size(cube.crossline_indexes)), cube.inline_indexes, cube.il_stride, cube.offset_count, data); elseif strcmpi(dir, 'xline') segy_read_write_line_mex(cube, n, max(size(cube.inline_indexes)), cube.crossline_indexes, cube.xl_stride, cube.offset_count, data ); else error('Only iline and xline are valid directions.'); end end % Goal: % Fast reading of trace header words in segy file. % % Algorithm: % Use keyword as specified in available_headers. If byte location is % known byte2headerword converts to keyword. % % Inputs: % filename Filename of segyfile % headword Name of header word to read (example: 'cdp') % notype Optional. If number format is different than set in segy % header this can be set by notype. Valid numbers are 1,2,3,5 % and 8 as spesified by SEG-Y rev 1 standard. % % Output: % name meaning % tr_heads Header values function [tr_heads, notrace] = get_header(filename, headword, notype) if exist(filename, 'file') ~= 2 error('File does not exist') end % notype isn't really use so we ignore it (for interface compatilibty) if ischar(headword) headword = TraceField.(headword); end headword = int32(headword); [x, y] = segy_get_header_mex(filename, headword); tr_heads = x; notrace = y; end % [data,dt,notype] = get_traces(filename,n1,n2,notype) % % Goal: % Fast reading of traces in segy volume. Not for front-end use. Use % get_line / get_slice / get_subcube instead. % % Algorithm: % % % Inputs: % filename Filename of segyfile % n1 (Optional) First trace. If no first and last trace is % specified, all file is read. % n2 (Optional) Last trace. % notype Optional. If number format is different than set in segy % header this can be set by notype. Valid numbers are 1,2,3,5 % and 8 as spesified by SEG-Y rev 1 standard. % % Output: % data Traces read from file % dt Sample interval % notype Number format function [data, dt, notype] = get_traces( filename, n1, n2, notype ) if exist(filename, 'file') ~= 2 error('File does not exist') end if nargin < 2 n1 = 1; end if nargin < 3 n2 = 0; end if nargin < 4 notype = -1; end [data, dt, notype] = segy_get_traces_mex( filename, n1 - 1, n2 - 1, notype ); end %function ntraces = get_ntraces(filename); % % Goal % Count the number of traces in a segy file % % Inputs: % filename Filename of segyfile % % Output: % notrace number of traces % return 0 in case of error function notrace = get_ntraces( filename ) if exist(filename, 'file') ~= 2 error('File does not exist') end notrace = segy_get_ntraces_mex( filename ); end % Goal: % Interpret segy cube as a 3D cube and save information needed to access % the segy file as a cube in terms of inline and crossline numbers. % % inputs: % filename filename of segy file % il_word bytenumber or header word for inline number % xl_word bytenumber or header word for crossline number % t0 Time (ms) / depth (m) of first sample. Optional (default = 0) % % output: % segycube struct needed to access segy file as a cube. function segycube = interpret_segycube(filename, il_word, xl_word, t0) if exist(filename, 'file') ~= 2 error('File does not exist') end if nargin < 4 t0 = 0; end if nargin < 3 xl_word = TraceField.Crossline3D; end if nargin < 2 il_word = TraceField.Inline3D; end % for compatibility with old code; if argument is passed as a % string, first convert to an enum, then pass that enum to the % constructor if ischar(il_word) il_word = TraceField.(il_word); end if ischar(xl_word) xl_word = TraceField.(xl_word); end segycube = SegySpec(filename, il_word, xl_word, t0); end % Goal: % Interpret segy cube as a 3D cube and save information needed to access % the segy file as a cube in terms of inline and crossline numbers. % % inputs: % name meaning % filename filename of segy file % offset bytenumber or header word for offset number % il_word bytenumber or header word for inline number % xl_word bytenumber or header word for crossline number % t0 Time (ms) / depth (m) of first sample. Optional (default = 0) % % output: % name meaning % segycube struct needed to access segy file as a cube. Used by % get_ps_line.m, put_ps_line.m and possibly friends function segycube = parse_ps_segycube(filename, offset, il_word, xl_word, t0) if ~exist('offset', 'var') || isempty(offset) offset = TraceField.offset; end if ~exist('il_word', 'var') || isempty(il_word) il_word = TraceField.Inline3D; end if ~exist('xl_word', 'var') || isempty(xl_word) xl_word = TraceField.Crossline3D; end if ~exist('t0', 'var') || isempty(t0) t0 = 0; end if ischar(il_word) il_word = TraceField.(il_word); end if ischar(xl_word) xl_word = TraceField.(xl_word); end if ischar(offset) offset = TraceField.(offset); end offset = int32(offset); il_word = int32(il_word); xl_word = int32(xl_word); t0 = double(t0); segycube = Segy.interpret_segycube(filename, il_word, xl_word, t0); offsets = segy_get_offsets_mex(segycube, offset, il_word, xl_word); segycube.offset = offsets; end % Goal: % Read an inline / crosline from a cube. % % Inputs: % cube Data as an interpreted segy cube from % 'interpret_segycube.m' % dir Direction of desired line (iline / xline) as a string % n Inline / crossline number % % Output: % data Extracted line function data = get_ps_line(cube, dir, n) if nargin < 3 error('Too few arguments. Usage: Segy.get_ps_line(cube, dir, n)'); end if strcmpi(dir, 'iline') len = max(size(cube.crossline_indexes)); ix = cube.inline_indexes; st = cube.il_stride; elseif strcmpi(dir, 'xline') len = max(size(cube.inline_indexes)); ix = cube.crossline_indexes; st = cube.xl_stride; else error('Only iline and xline are valid directions.'); end tmp = segy_read_write_ps_line_mex(cube, n, len, ix, st); tmp = reshape(tmp, [], cube.offset_count); nt = length(cube.t); data = permute(reshape(tmp, nt, size(tmp, 1)/nt, []), [1 3 2]); end function data = put_ps_line(cube, data, dir, n) if nargin < 4 error('Too few arguments. Usage: Segy.put_ps_line(cube, data, dir, n)'); end if strcmpi(dir, 'iline') len = max(size(cube.crossline_indexes)); ix = cube.inline_indexes; st = cube.il_stride; elseif strcmpi(dir, 'xline') len = max(size(cube.inline_indexes)); ix = cube.crossline_indexes; st = cube.xl_stride; else error('Only iline and xline are valid directions.'); end tmp = permute( data, [1 3 2] ); segy_read_write_ps_line_mex( cube, n, len, ix, st, tmp ); end function data = get_cube(sc) data = Segy.get_traces(sc.filename); if sc.trace_sorting_format == TraceSortingFormat.iline data = reshape( data, size( sc.sample_indexes, 1 ), size( sc.xline, 1 ), size( sc.iline, 1 ) ); elseif sc.trace_sorting_format == TraceSortingFormat.xline data = reshape( data, size( sc.sample_indexes, 1 ), size( sc.iline, 1 ), size( sc.xline, 1 ) ); else warning('Sorting was not set properly. Data returned as single long line'); end end function [data, notype] = put_traces(filename, data, n1, n2, notype) if exist(filename, 'file') ~= 2 error('File does not exist') end if nargin < 2 error('Too few arguments. Usage: put_traces( filename, data, (optional): n1, n2, notype)') end if nargin < 3 n1 = 1; end if nargin < 4 n2 = 0; end if nargin < 5 notype = -1; end % matlab uses 1-indexing, but C wants its positions 0-indexed. [data, notype] = segy_put_traces_mex( filename, data, n1 - 1, n2 - 1, notype ); end % function SegyHeader = get_segy_header(filename) % % Goal: % Read segy header. Extended textual headers are not read % % Inputs: % filename Filename of segyfile % % Output: % SegyHeader Struct with entire segy header function SegyHeader = get_segy_header(filename) if exist(filename, 'file') ~= 2 error('File does not exist') end [ebcdic, bin] = segy_get_segy_header_mex( filename ); SegyHeader.ebcdic = ebcdic; SegyHeader.JobIdNumber = segy_get_bfield_mex( bin, 3201 ); SegyHeader.LineNumber = segy_get_bfield_mex( bin, 3205 ); SegyHeader.ReelNumber = segy_get_bfield_mex( bin, 3209 ); SegyHeader.NumberOfTracesPerEnsemble = segy_get_bfield_mex( bin, 3213 ); SegyHeader.NumberOfAuxTracesPerEnsemble = segy_get_bfield_mex( bin, 3215 ); SegyHeader.SampleInterval = segy_get_bfield_mex( bin, 3217 ); SegyHeader.SampleIntervalOriginal = segy_get_bfield_mex( bin, 3219 ); SegyHeader.NumberOfSamples = segy_get_bfield_mex( bin, 3221 ); SegyHeader.NumberOfSamplesOriginal = segy_get_bfield_mex( bin, 3223 ); SegyHeader.SampleFormat = 0; switch segy_get_bfield_mex( bin, 3225 ) case 1 SegyHeader.SampleFormat = 'IBM32'; case 2 SegyHeader.SampleFormat = 'INT32'; case 3 SegyHeader.SampleFormat = 'INT16'; case 4 SegyHeader.SampleFormat = 'Obsolete'; case 5 SegyHeader.SampleFormat = 'IEEE32'; case 6 SegyHeader.SampleFormat = 'NotUsed'; case 7 SegyHeader.SampleFormat = 'NotUsed'; case 8 SegyHeader.SampleFormat = 'INT8'; end SegyHeader.EnsembleFold = segy_get_bfield_mex( bin, 3227 ); SegyHeader.TraceSortingCode = 0; switch segy_get_bfield_mex( bin, 3229 ) case -1 SegyHeader.TraceSortingCode = 'Other'; case 0 SegyHeader.TraceSortingCode = 'Unknown'; case 1 SegyHeader.TraceSortingCode = 'AsRecorded'; case 2 SegyHeader.TraceSortingCode = 'CDP'; case 3 SegyHeader.TraceSortingCode = 'SingleFoldContinuousProfile'; case 4 SegyHeader.TraceSortingCode = 'HorizontallyStacked'; case 5 SegyHeader.TraceSortingCode = 'CommonSourcePoint'; case 6 SegyHeader.TraceSortingCode = 'CommonReceiverPoint'; case 7 SegyHeader.TraceSortingCode = 'CommonOffsetPoint'; case 8 SegyHeader.TraceSortingCode = 'CommonMidPoint'; case 9 SegyHeader.TraceSortingCode = 'CommonConversionPoint'; end SegyHeader.VerticalSumCode = [num2str(segy_get_bfield_mex( bin, 3231 )), '_Sum']; SegyHeader.SweepFrequencyAtStart = segy_get_bfield_mex( bin, 3233 ); SegyHeader.SweepFrequencyAtEnd = segy_get_bfield_mex( bin, 3235 ); SegyHeader.SweepLength = segy_get_bfield_mex( bin, 3237 ); SegyHeader.SweepTypeCode = 0; switch segy_get_bfield_mex( bin, 3239 ) case 1 SegyHeader.SweepTypeCode = 'Linear'; case 2 SegyHeader.SweepTypeCode = 'Parabolic'; case 3 SegyHeader.SweepTypeCode = 'Exponential'; case 4 SegyHeader.SweepTypeCode = 'Other'; end SegyHeader.TraceNoOfSweepChannel = segy_get_bfield_mex( bin, 3241 ); SegyHeader.SweepTraceTaperLenghtStart = segy_get_bfield_mex( bin, 3243 ); SegyHeader.SweepTraceTaperLenghtEnd = segy_get_bfield_mex( bin, 3245 ); SegyHeader.TaperType = 0; switch segy_get_bfield_mex( bin, 3247 ) case 1 SegyHeader.TaperType = 'Linear'; case 2 SegyHeader.TaperType = 'Cos^2'; case 3 SegyHeader.TaperType = 'Other'; end SegyHeader.CorrelatedDataTraces = 0; switch segy_get_bfield_mex( bin, 3249 ) case 1 SegyHeader.CorrelatedDataTraces = 'No'; case 2 SegyHeader.CorrelatedDataTraces = 'Yes'; end SegyHeader.BinaryGainRecovered = 0; switch segy_get_bfield_mex( bin, 3251 ) case 1 SegyHeader.BinaryGainRecovered = 'Yes'; case 2 SegyHeader.BinaryGainRecovered = 'No'; end SegyHeader.AmplitudeRecoveryMethod = 0; switch segy_get_bfield_mex( bin, 3253 ) case 1 SegyHeader.AmplitudeRecoveryMethod = 'None'; case 2 SegyHeader.AmplitudeRecoveryMethod = 'SphericalDivergence'; case 3 SegyHeader.AmplitudeRecoveryMethod = 'AGC'; case 4 SegyHeader.AmplitudeRecoveryMethod = 'Other'; end SegyHeader.MeasurementSystem = 0; switch segy_get_bfield_mex( bin, 3255 ) case 1 SegyHeader.MeasurementSystem = 'Meter'; case 2 SegyHeader.MeasurementSystem = 'Feet'; end SegyHeader.ImpulseSignalPolarity = 0; switch segy_get_bfield_mex( bin, 3257 ) case 1 SegyHeader.ImpulseSignalPolarity = 'IncreasePressureNegativeNumber'; case 2 SegyHeader.ImpulseSignalPolarity = 'IncreasePressurePositiveNumber'; end SegyHeader.VibratorPolarityCode = 0; switch segy_get_bfield_mex( bin, 3259 ) case 1 SegyHeader.VibratorPolarityCode = '337.5-22.5'; case 2 SegyHeader.VibratorPolarityCode = '22.5-67.5'; case 3 SegyHeader.VibratorPolarityCode = '67.5-112.5'; case 4 SegyHeader.VibratorPolarityCode = '112.5-157.5'; case 5 SegyHeader.VibratorPolarityCode = '157.5-202.5'; case 6 SegyHeader.VibratorPolarityCode = '202.5-247.5'; case 7 SegyHeader.VibratorPolarityCode = '247.5-292.5'; case 8 SegyHeader.VibratorPolarityCode = '292.5-337.5'; end SegyHeader.FormatRevisionNumber = segy_get_bfield_mex( bin, 3501 ); SegyHeader.FixedLengthTraceFlag = segy_get_bfield_mex( bin, 3503 ); SegyHeader.NumberOfExtTextHeaders = segy_get_bfield_mex( bin, 3505 ); end % [tr_heads,notrace] = get_trace_header(filename,itrace); % % Goal: % Read the full trace header of the trace itrace % % Inputs: % filename Filename of segyfile % itrace trace number % % Output: % tr_heads Header values % notrace number of traces in segy file % function [tr_heads, notrace] = get_trace_header(filename, itrace) [header, notrace] = segy_get_trace_header_mex( filename, itrace ); % read trace header tr_heads.TraceSequenceLine = segy_get_field_mex( header, 1 ); tr_heads.TraceSequenceFile = segy_get_field_mex( header, 5 ); tr_heads.FieldRecord = segy_get_field_mex( header, 9 ); tr_heads.TraceNumber = segy_get_field_mex( header, 13 ); tr_heads.EnergySourcePoint = segy_get_field_mex( header, 17 ); tr_heads.cdp = segy_get_field_mex( header, 21 ); tr_heads.cdpTrace = segy_get_field_mex( header, 25 ); tr_heads.TraceIdenitifactionCode = segy_get_field_mex( header, 29 ); tr_heads.NSummedTraces = segy_get_field_mex( header, 31 ); tr_heads.NStackedTraces = segy_get_field_mex( header, 33 ); tr_heads.DataUse = segy_get_field_mex( header, 35 ); tr_heads.offset = segy_get_field_mex( header, 37 ); tr_heads.ReceiverGroupElevation = segy_get_field_mex( header, 41 ); tr_heads.SourceSurfaceElevation = segy_get_field_mex( header, 45 ); tr_heads.SourceDepth = segy_get_field_mex( header, 49 ); tr_heads.ReceiverDatumElevation = segy_get_field_mex( header, 53 ); tr_heads.SourceDatumElevation = segy_get_field_mex( header, 57 ); tr_heads.SourceWaterDepth = segy_get_field_mex( header, 61 ); tr_heads.GroupWaterDepth = segy_get_field_mex( header, 65 ); tr_heads.ElevationScalar = segy_get_field_mex( header, 69 ); tr_heads.SourceGroupScalar = segy_get_field_mex( header, 71 ); tr_heads.SourceX = segy_get_field_mex( header, 73 ); tr_heads.SourceY = segy_get_field_mex( header, 77 ); tr_heads.GroupX = segy_get_field_mex( header, 81 ); tr_heads.GroupY = segy_get_field_mex( header, 85 ); tr_heads.CoordinateUnits = segy_get_field_mex( header, 89 ); tr_heads.WeatheringVelocity = segy_get_field_mex( header, 91 ); tr_heads.SubWeatheringVelocity = segy_get_field_mex( header, 93 ); tr_heads.SourceUpholeTime = segy_get_field_mex( header, 95 ); tr_heads.GroupUpholeTime = segy_get_field_mex( header, 97 ); tr_heads.SourceStaticCorrection = segy_get_field_mex( header, 99 ); tr_heads.GroupStaticCorrection = segy_get_field_mex( header, 101 ); tr_heads.TotalStaticApplied = segy_get_field_mex( header, 103 ); tr_heads.LagTimeA = segy_get_field_mex( header, 105 ); tr_heads.LagTimeB = segy_get_field_mex( header, 107 ); tr_heads.DelayRecordingTime = segy_get_field_mex( header, 109 ); tr_heads.MuteTimeStart = segy_get_field_mex( header, 111 ); tr_heads.MuteTimeEND = segy_get_field_mex( header, 113 ); tr_heads.ns = segy_get_field_mex( header, 115 ); tr_heads.dt = segy_get_field_mex( header, 117 ); tr_heads.GainType = segy_get_field_mex( header, 119 ); tr_heads.InstrumentGainConstant = segy_get_field_mex( header, 121 ); tr_heads.InstrumentInitialGain = segy_get_field_mex( header, 123 ); tr_heads.Correlated = segy_get_field_mex( header, 125 ); tr_heads.SweepFrequenceStart = segy_get_field_mex( header, 127 ); tr_heads.SweepFrequenceEnd = segy_get_field_mex( header, 129 ); tr_heads.SweepLength = segy_get_field_mex( header, 131 ); tr_heads.SweepType = segy_get_field_mex( header, 133 ); tr_heads.SweepTraceTaperLengthStart = segy_get_field_mex( header, 135 ); tr_heads.SweepTraceTaperLengthEnd = segy_get_field_mex( header, 137 ); tr_heads.TaperType = segy_get_field_mex( header, 139 ); tr_heads.AliasFilterFrequency = segy_get_field_mex( header, 141 ); tr_heads.AliasFilterSlope = segy_get_field_mex( header, 143 ); tr_heads.NotchFilterFrequency = segy_get_field_mex( header, 145 ); tr_heads.NotchFilterSlope = segy_get_field_mex( header, 147 ); tr_heads.LowCutFrequency = segy_get_field_mex( header, 149 ); tr_heads.HighCutFrequency = segy_get_field_mex( header, 151 ); tr_heads.LowCutSlope = segy_get_field_mex( header, 153 ); tr_heads.HighCutSlope = segy_get_field_mex( header, 155 ); tr_heads.YearDataRecorded = segy_get_field_mex( header, 157 ); tr_heads.DayOfYear = segy_get_field_mex( header, 159 ); tr_heads.HourOfDay = segy_get_field_mex( header, 161 ); tr_heads.MinuteOfHour = segy_get_field_mex( header, 163 ); tr_heads.SecondOfMinute = segy_get_field_mex( header, 165 ); tr_heads.TimeBaseCode = segy_get_field_mex( header, 167 ); tr_heads.TraceWeightningFactor = segy_get_field_mex( header, 169 ); tr_heads.GeophoneGroupNumberRoll1 = segy_get_field_mex( header, 171 ); tr_heads.GeophoneGroupNumberFirstTraceOrigField = segy_get_field_mex( header, 173 ); tr_heads.GeophoneGroupNumberLastTraceOrigField = segy_get_field_mex( header, 175 ); tr_heads.GapSize = segy_get_field_mex( header, 177 ); tr_heads.OverTravel = segy_get_field_mex( header, 179 ); tr_heads.cdpX = segy_get_field_mex( header, 181 ); tr_heads.cdpY = segy_get_field_mex( header, 185 ); tr_heads.Inline3D = segy_get_field_mex( header, 189 ); tr_heads.Crossline3D = segy_get_field_mex( header, 193 ); tr_heads.ShotPoint = segy_get_field_mex( header, 197 ); tr_heads.ShotPointScalar = segy_get_field_mex( header, 201 ); tr_heads.TraceValueMeasurementUnit = segy_get_field_mex( header, 203 ); tr_heads.TransductionConstantMantissa = segy_get_field_mex( header, 205 ); tr_heads.TransductionConstantPower = segy_get_field_mex( header, 209 ); tr_heads.TransductionUnit = segy_get_field_mex( header, 211 ); tr_heads.TraceIdentifier = segy_get_field_mex( header, 213 ); tr_heads.ScalarTraceHeader = segy_get_field_mex( header, 215 ); tr_heads.SourceType = segy_get_field_mex( header, 217 ); tr_heads.SourceEnergyDirectionMantissa = segy_get_field_mex( header, 219 ); tr_heads.SourceEnergyDirectionExponent = segy_get_field_mex( header, 223 ); tr_heads.SourceMeasurementMantissa = segy_get_field_mex( header, 225 ); tr_heads.SourceMeasurementExponent = segy_get_field_mex( header, 229 ); tr_heads.SourceMeasurementUnit = segy_get_field_mex( header, 231 ); tr_heads.UnassignedInt1 = segy_get_field_mex( header, 233 ); tr_heads.UnassignedInt2 = segy_get_field_mex( header, 237 ); end % put_headers(filename,headers,headword) % % Goal: % Fast writing of trace header words in segy file. % % Inputs: % filename Filename of segyfile % headers Array of headervalues or single headervalue. Will be % written to all trace headers. If length of array is % different from number of traces in file an error will be % thrown. % headword Name of header word to be written (example: 'cdp') % function put_headers(filename, headers, headword) ntraces = Segy.get_ntraces( filename ); if and( ~isscalar(headers), max(size( headers )) ~= ntraces ) error( 'Inconsistent dimensions of header values' ) end % if single header value, create a traces-sized vector of that % header value, so that segy_put_headers_mex can always assume array if isscalar( headers ) headers = ones( 1, ntraces ) * headers; end if ischar(headword) headword = TraceField.(headword); end segy_put_headers_mex( filename, headers, int32(headword) ); end end end segyio-1.5.2/mex/segy_get_header_mex.c0000664000372000037200000000213013253720451017342 0ustar travistravis#include #include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1 = ""; char* msg2 = ""; int err; const char* filename = mxArrayToString( prhs[ 0 ] ); segy_file* fp = segyfopen( prhs[ 0 ], "rb" ); if( !fp ) goto cleanup; int field = mxGetScalar( prhs[ 1 ] ); struct segy_file_format fmt = filefmt( fp ); plhs[0] = mxCreateNumericMatrix( 1, fmt.traces, mxINT32_CLASS, mxREAL ); int* out = mxGetData( plhs[ 0 ] ); err = segy_field_forall( fp, field, 0, fmt.traces, 1, /* start, stop, step */ out, fmt.trace0, fmt.trace_bsize ); int no = errno; segy_close( fp ); if( err != SEGY_OK ) mexErrMsgIdAndTxt( "segy:get_header:forall", strerror( errno ) ); plhs[ 1 ] = mxCreateDoubleScalar( fmt.traces ); return; cleanup: mexErrMsgIdAndTxt( "segy:get_header:fopen", strerror( errno ) ); } segyio-1.5.2/mex/segy_get_trace_header_mex.c0000664000372000037200000000202513253720451020523 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1; char* msg2; int err; segy_file* fp = segyfopen( prhs[ 0 ], "rb" ); struct segy_file_format fmt = filefmt( fp ); int traceno = mxGetScalar( prhs[ 1 ] ); if( traceno > fmt.traces ) mexErrMsgIdAndTxt( "segy:get_trace_header:bounds", "Requested trace header does not exist in this file." ); mwSize dims[ 1 ] = { SEGY_TRACE_HEADER_SIZE }; plhs[ 0 ] = mxCreateCharArray( 1, dims ); err = segy_traceheader( fp, traceno, mxGetData( plhs[ 0 ] ), fmt.trace0, fmt.trace_bsize ); segy_close( fp ); if( err != 0 ) { msg1 = "segy:get_trace_header:os"; msg2 = strerror( errno ); goto cleanup; } plhs[ 1 ] = mxCreateDoubleScalar( fmt.traces ); return; cleanup: mexErrMsgIdAndTxt( msg1, msg2 ); } segyio-1.5.2/mex/segyspec_mex.c0000664000372000037200000001166113253720451016057 0ustar travistravis#include #include "mex.h" #include #include "segyutil.h" mxArray *createPLHSStruct() { int nfields = 11; const char *fnames[nfields]; fnames[0] = "filename"; fnames[1] = "sample_format"; fnames[2] = "crossline_indexes"; fnames[3] = "inline_indexes"; fnames[4] = "sample_indexes"; fnames[5] = "trace_sorting_format"; fnames[6] = "offset_count"; fnames[7] = "first_trace_pos"; fnames[8] = "il_stride"; fnames[9] = "xl_stride"; fnames[10] = "trace_bsize"; mxArray *plhs = mxCreateStructMatrix(1,1,nfields,fnames); return plhs; } void checkInputOutputSizes(int nlhs, int nrhs ) { /* check for proper number of arguments */ if(nrhs!=5) { mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs","Four inputs required."); } if(nlhs!=1) { mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs","One output required."); } } void checkInputOutput(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) { checkInputOutputSizes(nlhs, nrhs); /* First input must be a string */ if ( mxIsChar(prhs[0]) != 1) { mexErrMsgIdAndTxt("SegyIo:segyspec:inputNotString", "Input must be a string."); } /* First input must be a row vector */ if (mxGetM(prhs[0])!=1) { mexErrMsgIdAndTxt("SegyIo:segyspec:inputNotVector", "Input must be a row vector."); } /* make sure the second input argument is int */ if( !mxIsNumeric(prhs[1]) || mxGetNumberOfElements(prhs[1])!=1 ) { mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a numeric."); } /* make sure the third input argument is int */ if( !mxIsNumeric(prhs[2]) || mxGetNumberOfElements(prhs[2])!=1 ) { mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a numeric."); } /* make sure the fourth input argument is double */ if( !mxIsDouble(prhs[3]) || mxGetNumberOfElements(prhs[3])!=1 ) { mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a double."); } /* make sure the fifth input argument is double */ if( !mxIsDouble(prhs[4]) || mxGetNumberOfElements(prhs[4])!=1 ) { mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a double."); } } /* The gateway function */ void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { plhs[0] = createPLHSStruct(); checkInputOutput(nlhs, plhs, nrhs, prhs); char *filename = mxArrayToString(prhs[0]); int il = (int)mxGetScalar(prhs[1]); int xl = (int)mxGetScalar(prhs[2]); double t0 = mxGetScalar(prhs[3]); double dt = mxGetScalar(prhs[4]); SegySpec spec; int errc = segyCreateSpec(&spec, filename, il, xl, t0 * 1000.0, dt); if (errc != 0) { goto FAILURE; } mxSetFieldByNumber(plhs[0], 0, 0, mxCreateString(spec.filename)); mxSetFieldByNumber(plhs[0], 0, 1, mxCreateDoubleScalar(spec.sample_format)); mxArray *crossline_indexes = mxCreateDoubleMatrix(spec.crossline_count, 1, mxREAL); double *crossline_indexes_ptr = mxGetPr(crossline_indexes); for (int i = 0; i < spec.crossline_count; i++) { crossline_indexes_ptr[i] = spec.crossline_indexes[i]; } mxSetFieldByNumber(plhs[0], 0, 2, crossline_indexes); mxArray *inline_indexes = mxCreateDoubleMatrix(spec.inline_count, 1, mxREAL); double *inline_indexes_ptr = mxGetPr(inline_indexes); for (int i = 0; i < spec.inline_count; i++) { inline_indexes_ptr[i] = spec.inline_indexes[i]; } mxSetFieldByNumber(plhs[0], 0, 3, inline_indexes); mxArray *mx_sample_indexes = mxCreateDoubleMatrix(spec.sample_count,1, mxREAL); double *mx_sample_indexes_ptr = mxGetPr(mx_sample_indexes); for (int i = 0; i < spec.sample_count; i++) { mx_sample_indexes_ptr[i] = spec.sample_indices[i]; } mxSetFieldByNumber(plhs[0], 0, 4, mx_sample_indexes); mxSetFieldByNumber(plhs[0], 0, 5, mxCreateDoubleScalar(spec.trace_sorting_format)); mxSetFieldByNumber(plhs[0], 0, 6, mxCreateDoubleScalar(spec.offset_count)); mxSetFieldByNumber(plhs[0], 0, 7, mxCreateDoubleScalar(spec.first_trace_pos)); mxSetFieldByNumber(plhs[0], 0, 8, mxCreateDoubleScalar(spec.il_stride)); mxSetFieldByNumber(plhs[0], 0, 9, mxCreateDoubleScalar(spec.xl_stride)); mxSetFieldByNumber(plhs[0], 0, 10, mxCreateDoubleScalar(spec.trace_bsize)); if (spec.crossline_indexes != NULL) free(spec.crossline_indexes); if (spec.inline_indexes != NULL) free(spec.inline_indexes); free(spec.sample_indices); if (spec.filename != NULL) free(spec.filename); mxFree(filename); return; FAILURE: { int nfields = 1; const char *fnames[nfields]; fnames[0] = "error"; plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames); } mxFree(filename); } segyio-1.5.2/mex/segyutil.c0000664000372000037200000001473413253720451015235 0ustar travistravis#include #include #include #include #include "segyutil.h" static char* copyString(const char* path) { size_t size = strlen(path) + 1; char* path_copy = malloc(size); memcpy(path_copy, path, size); return path_copy; } int segyCreateSpec(SegySpec* spec, const char* file, unsigned int inline_field, unsigned int crossline_field, float t0, float dt) { int errc = 0; segy_file* fp = segy_open( file, "rb" ); if (fp == NULL) { fprintf(stderr, "Unable to open file: '%s'\n", file); return -1; } spec->sample_indices = NULL; spec->inline_indexes = NULL; spec->crossline_indexes = NULL; char header[ SEGY_BINARY_HEADER_SIZE ]; errc = segy_binheader( fp, header ); if (errc!=0) { goto CLEANUP; } spec->filename = copyString(file); spec->sample_format = segy_format( header ); spec->sample_count = segy_samples( header ); spec->sample_indices = malloc(sizeof(float) * spec->sample_count); errc = segy_sample_indices(fp, t0, dt, spec->sample_count, spec->sample_indices ); if (errc != 0) { goto CLEANUP; } const long trace0 = segy_trace0( header ); spec->trace_bsize = segy_trace_bsize( segy_samples( header ) ); int traces; errc = segy_traces(fp, &traces, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } errc = segy_offsets(fp, inline_field, crossline_field, traces, &spec->offset_count, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } errc = segy_sorting(fp, inline_field, crossline_field, SEGY_TR_OFFSET, &spec->trace_sorting_format, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } int* l1; int* l2; int field; if (spec->trace_sorting_format == SEGY_INLINE_SORTING) { field = crossline_field; l1 = &spec->inline_count; l2 = &spec->crossline_count; } else if (spec->trace_sorting_format == SEGY_CROSSLINE_SORTING) { field = inline_field; l2 = &spec->inline_count; l1 = &spec->crossline_count; } else { fprintf(stderr, "Unknown sorting\n"); goto CLEANUP; } errc = segy_count_lines(fp, field, spec->offset_count, l1, l2, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } spec->inline_indexes = malloc(sizeof(int) * spec->inline_count); spec->crossline_indexes = malloc(sizeof(int) * spec->crossline_count); errc = segy_inline_indices(fp, inline_field, spec->trace_sorting_format, spec->inline_count, spec->crossline_count, spec->offset_count, spec->inline_indexes, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } errc = segy_crossline_indices(fp, crossline_field, spec->trace_sorting_format, spec->inline_count, spec->crossline_count, spec->offset_count, spec->crossline_indexes, trace0, spec->trace_bsize); if (errc != 0) { goto CLEANUP; } spec->first_trace_pos = segy_trace0( header ); errc = segy_inline_stride(spec->trace_sorting_format, spec->inline_count, &spec->il_stride); if (errc != 0) { goto CLEANUP; } errc = segy_crossline_stride(spec->trace_sorting_format, spec->crossline_count, &spec->xl_stride); if (errc != 0) { goto CLEANUP; } segy_close(fp); return 0; CLEANUP: if (spec->crossline_indexes != NULL) free(spec->crossline_indexes); if (spec->inline_indexes != NULL) free(spec->inline_indexes); free(spec->sample_indices); free(spec->filename); segy_close(fp); return errc; } static int getMaxDim(mxArray* arr){ int n = mxGetN(arr); int m = mxGetM(arr); int max = m; if (n>m) max = n; return max; } void recreateSpec(SegySpec *spec, const mxArray* mex_spec) { spec->filename = mxArrayToString(mxGetProperty(mex_spec, 0, "filename")); spec->sample_format = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "sample_format")); spec->trace_sorting_format = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "trace_sorting_format")); spec->offset_count = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "offset_count")); spec->first_trace_pos = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "first_trace_pos")); spec->il_stride = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "il_stride")); spec->xl_stride = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "xl_stride")); spec->trace_bsize = (int)mxGetScalar(mxGetProperty(mex_spec, 0, "trace_bsize")); mxArray* crossline_indexes = mxGetProperty(mex_spec, 0, "crossline_indexes"); spec->crossline_count = getMaxDim(crossline_indexes); spec->crossline_indexes = mxGetData(crossline_indexes); mxArray* inline_indexes = mxGetProperty(mex_spec, 0, "inline_indexes"); spec->inline_count = getMaxDim(inline_indexes); spec->inline_indexes = mxGetData(inline_indexes); mxArray* sample_indexes = mxGetProperty(mex_spec, 0, "sample_indexes"); spec->sample_count = getMaxDim(sample_indexes); spec->sample_indices = mxGetData(sample_indexes); } struct segy_file_format buffmt( const char* binary ) { struct segy_file_format fmt; fmt.samples = segy_samples( binary ); fmt.trace_bsize = segy_trace_bsize( fmt.samples ); fmt.trace0 = segy_trace0( binary ); fmt.format = segy_format( binary ); fmt.traces = 0; return fmt; } struct segy_file_format filefmt( segy_file* fp ) { char binary[SEGY_BINARY_HEADER_SIZE]; int err = segy_binheader( fp, binary ); if( err != 0 ) mexErrMsgIdAndTxt( "segy:c:filemft", strerror( errno ) ); struct segy_file_format fmt = buffmt( binary ); err = segy_traces( fp, &fmt.traces, fmt.trace0, fmt.trace_bsize ); if( err == 0 ) return fmt; const char* msg1 = "segy:c:filefmt"; const char* msg2; if( err == SEGY_TRACE_SIZE_MISMATCH ) msg2 = "Number of traces not consistent with file size. File corrupt?"; else msg2 = strerror( errno ); mexErrMsgIdAndTxt( msg1, msg2 ); } segy_file* segyfopen( const mxArray* filename, const char* mode ) { const char* fname = mxArrayToString( filename ); segy_file* fp = segy_open( fname, mode ); int err = errno; mxFree( (void*)fname ); if( !fp ) mexErrMsgIdAndTxt( "segy:c:fopen", strerror( err ) ); return fp; } segyio-1.5.2/mex/segy_put_traces_mex.c0000664000372000037200000000335313253720451017434 0ustar travistravis#include #include #include #include "segyutil.h" #include "matrix.h" #include "mex.h" void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char* msg1; char* msg2; int err; segy_file* fp = segyfopen( prhs[ 0 ], "r+b" ); plhs[ 0 ] = mxDuplicateArray( prhs[ 1 ] ); int first_trace = mxGetScalar( prhs[ 2 ] ); int last_trace = mxGetScalar( prhs[ 3 ] ); int notype = mxGetScalar( prhs[ 4 ] ); struct segy_file_format fmt = filefmt( fp ); if( notype != -1 ) fmt.format = notype; // if last_trace was defaulted we assign it to the last trace in the file if( last_trace == -1 ) last_trace = fmt.traces - 1; int traces = 1 + (last_trace - first_trace); long long bufsize = (long long)fmt.samples * traces; if( first_trace > last_trace ) { msg1 = "segy:get_traces:bounds"; msg2 = "first trace must be smaller than last trace"; goto cleanup; } float* out = mxGetData( plhs[ 0 ] ); segy_from_native( fmt.format, fmt.samples * fmt.traces, out ); float* itr = out; for( int i = first_trace; i <= last_trace; ++i ) { err = segy_writetrace( fp, i, itr, fmt.trace0, fmt.trace_bsize ); itr += fmt.samples; if( err != 0 ) { msg1 = "segy:put_traces:segy_writetrace"; msg2 = strerror( errno ); fmt.traces = i; goto cleanup; } } segy_close( fp ); segy_to_native( fmt.format, bufsize, out ); plhs[ 1 ] = mxCreateDoubleScalar( fmt.format ); return; cleanup: segy_close( fp ); segy_to_native( fmt.format, bufsize, out ); mexErrMsgIdAndTxt( msg1, msg2 ); } segyio-1.5.2/python/0000775000372000037200000000000013253720451013743 5ustar travistravissegyio-1.5.2/python/examples/0000775000372000037200000000000013253720451015561 5ustar travistravissegyio-1.5.2/python/examples/write.py0000664000372000037200000000531313253720451017267 0ustar travistravisimport sys import segyio import numpy as np def main(): if len( sys.argv ) < 2: sys.exit("Usage: write.py [file]") filename = sys.argv[1] # the mode parameter is passed directly to C's fopen # opening the file for writing requires r+, not rw because rw would # truncate (effectively destroy) the file, and r+ would preserve the size with segyio.open( filename, "r+" ) as src: # read trace 0, then double all values trace = src.trace[0] trace *= 2 # write trace 0 back to disk src.trace[0] = trace # read trace 1, but re-use the memory for speed trace = src.trace[1, trace] # square all values. the trace is a plain numpy array trace = np.square(trace, trace) # write the trace back to disk, but at trace 2 src.trace[2] = trace # read every other trace, from 10 through 20 # then write them to every third step from 40 through 52 # i.e. 40, 43, 46... # slices yield a generator, so only one numpy array is created for tr, i in zip(src.trace[10:20:2], range(2,13,3)): src.trace[i] = tr # iterate over all traces in a file. this is a generator with a shared # buffer, so it's quite efficient tracesum = 0 for tr in src.trace: # accumulate the traces' 30th value tracesum += tr[30] print("Trace sum: {}".format(tracesum)) # write the iline at 2 to the iline at 3 sum3 = np.sum(src.iline[3]) src.iline[2] = src.iline[3] # flush to make sure our changes to the file are visible src.flush() sum2 = np.sum(src.iline[2]) print("Accumulates of inlines 2 and 3: {} -- {}".format(sum2, sum3)) # ilines too are plain numpy ndarrays, with trace-major addressing # i.e. iline[2,40] would be yield trace#2's 40th value iline = src.iline[2] # since ilines are numpy arrays they also support numpy operations iline = np.add(iline, src.iline[4]) # lines too have generator support, so we accumulate the 2nd trace's # 22nd value. linesum = 0 for line in src.iline: linesum += line[2,22] print("Inline sum: {}".format(linesum)) # xline access is identical to iline access linesum = 0 for line in src.xline: linesum += line[2,22] print("Crossline sum: {}".format(linesum)) # accessing a non-existing inline will raise a KeyError try: _ = src.iline[5000] sys.exit("Was able to access non-existing inline") except KeyError as e: print(str(e)) if __name__ == '__main__': main() segyio-1.5.2/python/examples/scan_min_max.py0000664000372000037200000000127613253720451020575 0ustar travistravisimport sys import segyio import numpy as np def main(): if len(sys.argv) < 2: sys.exit("Usage: {} [segyfile] ".format(sys.argv[0])) segyfile = sys.argv[1] min_value = sys.float_info.max max_value = sys.float_info.min with segyio.open(segyfile) as f: for trace in f.trace: local_min = np.nanmin(trace) local_max = np.nanmax(trace) if np.isfinite(local_min): min_value = min(local_min, min_value) if np.isfinite(local_max): max_value = max(local_max, max_value) print("min: {}".format(min_value)) print("max: {}".format(max_value)) if __name__ == '__main__': main() segyio-1.5.2/python/examples/copy-sub-cube.py0000664000372000037200000000260113253720451020607 0ustar travistravisimport sys import segyio # this program creates a new subcube, taking the first 5 lines in both # directions, and reduces the trace size to 20 samples def main(): if len(sys.argv) < 3: sys.exit("Usage: {} [source-file] [destination-file]".format(sys.argv[0])) sourcefile = sys.argv[1] destfile = sys.argv[2] with segyio.open(sourcefile) as src: spec = segyio.spec() spec.sorting = int(src.sorting) spec.format = int(src.format) spec.samples = range(50) spec.ilines = src.ilines[:5] spec.xlines = src.xlines[:5] with segyio.create(destfile, spec) as dst: # Copy all textual headers, including possible extended for i in range(1 + src.ext_headers): dst.text[i] = src.text[i] # copy the binary header, then insert the modifications needed for # the new shape dst.bin = src.bin dst.bin = { segyio.BinField.Samples: 50, segyio.BinField.Traces: 5 * 5 } # Copy all headers in the new inlines. Since we know we're copying # the five first we don't have to take special care to update # headers dst.header.iline = src.header.iline # the copy traces (in line mode) dst.iline = src.iline if __name__ == '__main__': main() segyio-1.5.2/python/examples/about.py0000664000372000037200000000360513253720451017251 0ustar travistravisimport sys from segyio import TraceField import segyio def list_byte_offset_names(): print("Available offsets and their corresponding byte value:") for x in TraceField.enums(): print(" {}: {}".format(str(x), x)) if __name__ == '__main__': if len(sys.argv) < 4: list_byte_offset_names() sys.exit("Usage: about.py [file] [inline] [crossline]") # we need a way to convert from run-time inline/crossline argument (as # text) to the internally used TraceField enum. Make a string -> TraceField # map and look up into that. this dictionary comprehension creates that fieldmap = {str(x).lower(): x for x in TraceField.enums()} filename = sys.argv[1] inline_name, crossline_name = sys.argv[2].lower(), sys.argv[3].lower() # exit if inline or crossline are unknown if inline_name not in fieldmap: list_byte_offset_names() sys.exit("Unknown inline field '{}'".format(sys.argv[2])) if crossline_name not in fieldmap: list_byte_offset_names() sys.exit("Unknown crossline field '{}'".format(sys.argv[3])) inline, crossline = fieldmap[inline_name], fieldmap[crossline_name] with segyio.open(filename, "r", inline, crossline) as f: print("About '{}':".format(filename)) print("Format type: {}".format(f.format)) print("Offset count: {}".format(f.offsets)) print("ilines: {}".format(", ".join(map(str, f.ilines)))) print("xlines: {}".format(", ".join(map(str, f.xlines)))) print("+------+") with segyio.open(filename, "r", crossline, inline) as f: # with swapped inline/crossline print("About '{}':".format(filename)) print("Format type: {}".format(f.format)) print("Offset count: {}".format(f.offsets)) print("ilines: {}".format(", ".join(map(str, f.ilines)))) print("xlines: {}".format(", ".join(map(str, f.xlines)))) segyio-1.5.2/python/examples/make-rotated-copies.py0000664000372000037200000000620013253720451021766 0ustar travistravisimport sys import os import shutil import itertools as itr import segyio import segyio.su as su def product(f): return itr.product(range(len(f.ilines)), range(len(f.xlines))) def pathjoin(prefix, path): dir, base = os.path.split(path) return os.path.join(dir, '-'.join((prefix, base))) # this program copies the source-file and creates eight copies, each with a # modified set of CDP-X and CDP-Y coordinates, rotating the field around the # north (increasing CDP-Y) axis. def main(): if len(sys.argv) < 2: sys.exit("Usage: {} [source-file] [destination-file]".format(sys.argv[0])) srcfile = sys.argv[1] dstfile = sys.argv[2] if len(sys.argv) > 2 else srcfile for pre in ['normal', 'acute', 'right', 'obtuse', 'straight', 'reflex', 'left', 'inv-acute']: fname = pathjoin(pre, dstfile) shutil.copyfile(srcfile, fname) with segyio.open(srcfile) as src, segyio.open(fname, 'r+') as dst: for i in range(1 + src.ext_headers): dst.text[i] = src.text[i] dst.bin = src.bin dst.trace = src.trace dst.header = src.header with segyio.open(pathjoin('normal', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = x trh[su.cdpy] = y trh[su.scalco] = 10 with segyio.open(pathjoin('acute', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[y + x * len(src.ilines)] trh[su.cdpx] = x + y trh[su.cdpy] = (100 - x) + y trh[su.scalco] = -10 with segyio.open(pathjoin('right', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = y trh[su.cdpy] = 100 - x trh[su.scalco] = 1 with segyio.open(pathjoin('obtuse', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = (100 - x) + y trh[su.cdpy] = (100 - x) - y trh[su.scalco] = 2 with segyio.open(pathjoin('straight', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = 100 - x trh[su.cdpy] = 100 - y trh[su.scalco] = -7 with segyio.open(pathjoin('reflex', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = 100 - (x + y) trh[su.cdpy] = 100 + (x - y) trh[su.scalco] = 7 with segyio.open(pathjoin('left', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = 100 - y trh[su.cdpy] = x trh[su.scalco] = 21 with segyio.open(pathjoin('inv-acute', dstfile), 'r+') as dst: for i, (x, y) in enumerate(product(src)): trh = dst.header[i] trh[su.cdpx] = 100 + x - y trh[su.cdpy] = x + y trh[su.scalco] = 100 if __name__ == '__main__': main() segyio-1.5.2/python/examples/make-ps-file.py0000664000372000037200000000500413253720451020404 0ustar travistravisimport sys import numpy as np import segyio def main(): if len(sys.argv) < 9: sys.exit(" ".join(["Usage: {} [file] [samples]", "[first iline] [last iline]", "[first xline] [last xline]", "[first offset] [last offset]"] ).format(sys.argv[0])) spec = segyio.spec() filename = sys.argv[1] # to create a file from nothing, we need to tell segyio about the structure of # the file, i.e. its inline numbers, crossline numbers, etc. You can also add # more structural information, This is the absolute minimal specification for a # N-by-M volume with K offsets volume spec.sorting = 2 spec.format = 1 spec.samples = range(int(sys.argv[2])) spec.ilines = range(*map(int, sys.argv[3:5])) spec.xlines = range(*map(int, sys.argv[5:7])) spec.offsets = range(*map(int, sys.argv[7:9])) if len(spec.offsets) == 0: spec.offsets = [1] with segyio.create(filename, spec) as f: # one inline consists of 50 traces # which in turn consists of 2000 samples start = 0.0 step = 0.00001 # fill a trace with predictable values: left-of-comma is the inline # number. Immediately right of comma is the crossline number # the rightmost digits is the index of the sample in that trace meaning # looking up an inline's i's jth crosslines' k should be roughly equal # to (offset*100) + i.j0k. trace = np.arange(start = start, stop = start + step * len(spec.samples), step = step, dtype = np.single) nx, no = len(spec.xlines), len(spec.offsets) # one inline is N traces concatenated. We fill in the xline number line = np.concatenate([trace + (xl / 100.0) for xl in spec.xlines]) line = line.reshape( (nx, len(spec.samples)) ) for ilindex, ilno in enumerate(spec.ilines): iline = line + ilno for tr, xlno in enumerate(spec.xlines): for offset_index, offset in enumerate(spec.offsets): ix = (ilindex * nx * no) + (tr * no) + offset_index f.trace[ix] = iline[tr] + (offset * 100) f.header[ix] = { segyio.TraceField.INLINE_3D: ilno, segyio.TraceField.CROSSLINE_3D: xlno, segyio.TraceField.offset: offset } if __name__ == '__main__': main() segyio-1.5.2/python/examples/make-file.py0000664000372000037200000000424713253720451017774 0ustar travistravisimport sys import numpy as np import segyio def main(): if len(sys.argv) < 7: sys.exit("Usage: {} [file] [samples] [first iline] [last iline] [first xline] [last xline]".format(sys.argv[0])) spec = segyio.spec() filename = sys.argv[1] # to create a file from nothing, we need to tell segyio about the structure of # the file, i.e. its inline numbers, crossline numbers, etc. You can also add # more structural information, but offsets etc. have sensible defautls. This is # the absolute minimal specification for a N-by-M volume spec.sorting = 2 spec.format = 1 spec.samples = range(int(sys.argv[2])) spec.ilines = range(*map(int, sys.argv[3:5])) spec.xlines = range(*map(int, sys.argv[5:7])) with segyio.create(filename, spec) as f: # one inline consists of 50 traces # which in turn consists of 2000 samples start = 0.0 step = 0.00001 # fill a trace with predictable values: left-of-comma is the inline # number. Immediately right of comma is the crossline number # the rightmost digits is the index of the sample in that trace meaning # looking up an inline's i's jth crosslines' k should be roughly equal # to i.j0k trace = np.arange(start = start, stop = start + step * len(spec.samples), step = step, dtype = np.single) # one inline is N traces concatenated. We fill in the xline number line = np.concatenate([trace + (xl / 100.0) for xl in spec.xlines]) line = line.reshape( (len(spec.xlines), len(spec.samples)) ) # write the line itself to the file # write the inline number in all this line's headers for ilno in spec.ilines: f.iline[ilno] = (line + ilno) f.header.iline[ilno] = { segyio.TraceField.INLINE_3D: ilno, segyio.TraceField.offset: 1 } # then do the same for xlines for xlno in spec.xlines: f.header.xline[xlno] = { segyio.TraceField.CROSSLINE_3D: xlno } if __name__ == '__main__': main() segyio-1.5.2/python/test/0000775000372000037200000000000013253720451014722 5ustar travistravissegyio-1.5.2/python/test/segy.py0000664000372000037200000010641313253720451016250 0ustar travistravisfrom __future__ import absolute_import try: from future_builtins import zip, map except ImportError: pass from types import GeneratorType import itertools import filecmp import shutil import numpy as np import pytest from pytest import approx from test import tmpfiles import segyio from segyio import TraceField, BinField from segyio._field import Field from segyio._line import Line from segyio._header import Header from segyio._trace import Trace def test_inline_4(): with segyio.open("test-data/small.sgy") as f: sample_count = len(f.samples) assert 50 == sample_count data = f.iline[4] assert 4.2, approx(data[0, 0], abs=1e-6) # middle sample assert 4.20024, approx(data[0, sample_count // 2 - 1], abs=1e-6) # last sample assert 4.20049, approx(data[0, -1], abs=1e-6) # middle xline middle_line = 2 # first sample assert 4.22, approx(data[middle_line, 0], abs=1e-5) # middle sample assert 4.22024, approx(data[middle_line, sample_count // 2 - 1], abs=1e-6) # last sample assert 4.22049, approx(data[middle_line, -1], abs=1e-6) # last xline last_line = (len(f.xlines) - 1) # first sample assert 4.24, approx(data[last_line, 0], abs=1e-5) # middle sample assert 4.24024, approx(data[last_line, sample_count // 2 - 1], abs=1e-6) # last sample assert 4.24049, approx(data[last_line, sample_count - 1], abs=1e-6) def test_xline_22(): with segyio.open("test-data/small.sgy") as f: data = f.xline[22] size = len(f.samples) # first iline # first sample assert 1.22, approx(data[0, 0], abs=1e-5) # middle sample assert 1.22024, approx(data[0, size // 2 - 1], abs=1e-6) # last sample assert 1.22049, approx(data[0, size - 1], abs=1e-6) # middle iline middle_line = 2 # first sample assert 3.22, approx(data[middle_line, 0], abs=1e-5) # middle sample assert 3.22024, approx(data[middle_line, size // 2 - 1], abs=1e-6) # last sample assert 3.22049, approx(data[middle_line, size - 1], abs=1e-6) # last iline last_line = len(f.ilines) - 1 # first sample assert 5.22, approx(data[last_line, 0], abs=1e-5) # middle sample assert 5.22024, approx(data[last_line, size // 2 - 1], abs=1e-6) # last sample assert 5.22049, approx(data[last_line, size - 1], abs=1e-6) def test_iline_slicing(): with segyio.open("test-data/small.sgy") as f: assert len(f.ilines) == sum(1 for _ in f.iline) assert len(f.ilines) == sum(1 for _ in f.iline[1:6]) assert len(f.ilines) == sum(1 for _ in f.iline[5:0:-1]) assert len(f.ilines) // 2 == sum(1 for _ in f.iline[0::2]) assert len(f.ilines) == sum(1 for _ in f.iline[1:]) assert 3 == sum(1 for _ in f.iline[::2]) assert 0 == sum(1 for _ in f.iline[12:24]) assert 3 == sum(1 for _ in f.iline[:4]) assert 2 == sum(1 for _ in f.iline[2:6:2]) def test_xline_slicing(): with segyio.open("test-data/small.sgy") as f: assert len(f.xlines) == sum(1 for _ in f.xline) assert len(f.xlines) == sum(1 for _ in f.xline[20:25]) assert len(f.xlines) == sum(1 for _ in f.xline[25:19:-1]) assert 3 == sum(1 for _ in f.xline[0::2]) assert 3 == sum(1 for _ in f.xline[::2]) assert len(f.xlines) == sum(1 for _ in f.xline[20:]) assert 0 == sum(1 for _ in f.xline[12:18]) assert 5 == sum(1 for _ in f.xline[:25]) assert 2 == sum(1 for _ in f.xline[:25:3]) def test_open_transposed_lines(): with segyio.open("test-data/small.sgy") as f: il = f.ilines xl = f.xlines with segyio.open("test-data/small.sgy", "r", segyio.TraceField.CROSSLINE_3D, segyio.TraceField.INLINE_3D) as f: assert list(il) == list(f.xlines) assert list(xl) == list(f.ilines) def test_file_info(): with segyio.open("test-data/small.sgy") as f: assert 2 == f.sorting assert 1 == f.offsets assert 1 == int(f.format) xlines = list(range(20, 25)) ilines = list(range(1, 6)) assert xlines == list(f.xlines) assert ilines == list(f.ilines) assert 25 == f.tracecount assert len(f.trace) == f.tracecount assert 50 == len(f.samples) def test_open_nostrict(): with segyio.open("test-data/small.sgy", strict=False): pass def test_open_ignore_geometry(): with segyio.open("test-data/small.sgy", ignore_geometry=True) as f: with pytest.raises(ValueError): _ = f.iline[0] def test_traces_slicing(): with segyio.open("test-data/small.sgy") as f: traces = list(map(np.copy, f.trace[0:6:2])) assert len(traces) == 3 assert traces[0][49] == f.trace[0][49] assert traces[1][49] == f.trace[2][49] assert traces[2][49] == f.trace[4][49] rev_traces = list(map(np.copy, f.trace[4::-2])) assert rev_traces[0][49] == f.trace[4][49] assert rev_traces[1][49] == f.trace[2][49] assert rev_traces[2][49] == f.trace[0][49] # make sure buffers can be reused buf = None for i, trace in enumerate(f.trace[0:6:2, buf]): assert np.array_equal(trace, traces[i]) def test_traces_offset(): with segyio.open("test-data/small-ps.sgy") as f: assert 2 == len(f.offsets) assert [1, 2] == list(f.offsets) # traces are laid out |l1o1 l1o2 l2o1 l2o2...| # where l = iline number and o = offset number # traces are not re-indexed according to offsets # see make-ps-file.py for value formula assert 101.01 == approx(f.trace[0][0], abs=1e-4) assert 201.01 == approx(f.trace[1][0], abs=1e-4) assert 101.02 == approx(f.trace[2][0], abs=1e-4) assert 201.02 == approx(f.trace[3][0], abs=1e-4) assert 102.01 == approx(f.trace[6][0], abs=1e-4) def test_headers_offset(): with segyio.open("test-data/small-ps.sgy") as f: il, xl = TraceField.INLINE_3D, TraceField.CROSSLINE_3D assert f.header[0][il] == f.header[1][il] assert f.header[1][il] == f.header[2][il] assert f.header[0][xl] == f.header[1][xl] assert not f.header[1][xl] == f.header[2][xl] def test_header_dict_methods(): with segyio.open("test-data/small.sgy") as f: assert 89 == len(list(f.header[0].keys())) assert 89 == len(list(f.header[1].values())) assert 89 == len(list(f.header[2].items())) assert 89 == len(list(f.header[3])) assert 0 not in f.header[0] assert 1 in f.header[0] assert segyio.su.cdpx in f.header[0] iter(f.header[0]) assert 30 == len(f.bin.keys()) assert 30 == len(list(f.bin.values())) assert 30 == len(list(f.bin.items())) assert 30 == len(f.bin) iter(f.bin) @tmpfiles("test-data/small-ps.sgy") def test_headers_line_offset(tmpdir): il, xl = TraceField.INLINE_3D, TraceField.CROSSLINE_3D with segyio.open(tmpdir / "small-ps.sgy", "r+") as f: f.header.iline[1, 2] = {il: 11} f.header.iline[1, 2] = {xl: 13} with segyio.open(tmpdir / "small-ps.sgy", strict=False) as f: assert f.header[0][il] == 1 assert f.header[1][il] == 11 assert f.header[2][il] == 1 assert f.header[0][xl] == 1 assert f.header[1][xl] == 13 assert f.header[2][xl] == 2 def test_attributes(): with segyio.open("test-data/small.sgy") as f: il = TraceField.INLINE_3D xl = TraceField.CROSSLINE_3D assert 1 == f.attributes(il)[0] assert 20 == f.attributes(xl)[0] ils = [(i // 5) + 1 for i in range(25)] attrils = list(map(int, f.attributes(il)[:])) assert ils == attrils xls = [(i % 5) + 20 for i in range(25)] attrxls = list(map(int, f.attributes(xl)[:])) assert xls == attrxls ils = [(i // 5) + 1 for i in range(25)][::-1] attrils = list(map(int, f.attributes(il)[::-1])) assert ils == attrils xls = [(i % 5) + 20 for i in range(25)][::-1] attrxls = list(map(int, f.attributes(xl)[::-1])) assert xls == attrxls assert f.header[0][il] == f.attributes(il)[0] f.mmap() assert f.header[0][il] == f.attributes(il)[0] ils = [(i // 5) + 1 for i in range(25)][1:21:3] attrils = list(map(int, f.attributes(il)[1:21:3])) assert ils == attrils xls = [(i % 5) + 20 for i in range(25)][2:17:5] attrxls = list(map(int, f.attributes(xl)[2:17:5])) assert xls == attrxls ils = [1, 2, 3, 4, 5] attrils = list(map(int, f.attributes(il)[[0, 5, 11, 17, 23]])) assert ils == attrils ils = [1, 2, 3, 4, 5] indices = np.asarray([0, 5, 11, 17, 23]) attrils = list(map(int, f.attributes(il)[indices])) assert ils == attrils def test_iline_offset(): with segyio.open("test-data/small-ps.sgy") as f: line1 = f.iline[1, 1] assert 101.01 == approx(line1[0][0], abs=1e-4) assert 101.02 == approx(line1[1][0], abs=1e-4) assert 101.03 == approx(line1[2][0], abs=1e-4) assert 101.01001 == approx(line1[0][1], abs=1e-4) assert 101.01002 == approx(line1[0][2], abs=1e-4) assert 101.02001 == approx(line1[1][1], abs=1e-4) line2 = f.iline[1, 2] assert 201.01 == approx(line2[0][0], abs=1e-4) assert 201.02 == approx(line2[1][0], abs=1e-4) assert 201.03 == approx(line2[2][0], abs=1e-4) assert 201.01001 == approx(line2[0][1], abs=1e-4) assert 201.01002 == approx(line2[0][2], abs=1e-4) assert 201.02001 == approx(line2[1][1], abs=1e-4) with pytest.raises(KeyError): _ = f.iline[1, 0] with pytest.raises(KeyError): _ = f.iline[1, 3] with pytest.raises(KeyError): _ = f.iline[100, 1] with pytest.raises(TypeError): _ = f.iline[1, {}] def test_iline_slice_fixed_offset(): with segyio.open("test-data/small-ps.sgy") as f: for i, ln in enumerate(f.iline[:, 1], 1): assert i + 100.01 == approx(ln[0][0], abs=1e-4) assert i + 100.02 == approx(ln[1][0], abs=1e-4) assert i + 100.03 == approx(ln[2][0], abs=1e-4) assert i + 100.01001 == approx(ln[0][1], abs=1e-4) assert i + 100.01002 == approx(ln[0][2], abs=1e-4) assert i + 100.02001 == approx(ln[1][1], abs=1e-4) def test_iline_slice_fixed_line(): with segyio.open("test-data/small-ps.sgy") as f: for i, ln in enumerate(f.iline[1, :], 1): off = i * 100 assert off + 1.01 == approx(ln[0][0], abs=1e-4) assert off + 1.02 == approx(ln[1][0], abs=1e-4) assert off + 1.03 == approx(ln[2][0], abs=1e-4) assert off + 1.01001 == approx(ln[0][1], abs=1e-4) assert off + 1.01002 == approx(ln[0][2], abs=1e-4) assert off + 1.02001 == approx(ln[1][1], abs=1e-4) def test_iline_slice_all_offsets(): with segyio.open("test-data/small-ps.sgy") as f: offs, ils = len(f.offsets), len(f.ilines) assert offs * ils == sum(1 for _ in f.iline[:, :]) assert offs * ils == sum(1 for _ in f.iline[:, ::-1]) assert offs * ils == sum(1 for _ in f.iline[::-1, :]) assert offs * ils == sum(1 for _ in f.iline[::-1, ::-1]) assert 0 == sum(1 for _ in f.iline[:, 10:12]) assert 0 == sum(1 for _ in f.iline[10:12, :]) assert (offs // 2) * ils == sum(1 for _ in f.iline[::2, :]) assert offs * (ils // 2) == sum(1 for _ in f.iline[:, ::2]) assert (offs // 2) * ils == sum(1 for _ in f.iline[::-2, :]) assert offs * (ils // 2) == sum(1 for _ in f.iline[:, ::-2]) assert (offs // 2) * (ils // 2) == sum(1 for _ in f.iline[::2, ::2]) assert (offs // 2) * (ils // 2) == sum(1 for _ in f.iline[::2, ::-2]) assert (offs // 2) * (ils // 2) == sum(1 for _ in f.iline[::-2, ::2]) def test_gather_mode(): with segyio.open("test-data/small-ps.sgy") as f: empty = np.empty(0, dtype=np.single) # should raise with pytest.raises(KeyError): assert np.array_equal(empty, f.gather[2, 3, 3]) with pytest.raises(KeyError): assert np.array_equal(empty, f.gather[2, 5, 1]) with pytest.raises(KeyError): assert np.array_equal(empty, f.gather[5, 2, 1]) assert np.array_equal(f.trace[10], f.gather[2, 3, 1]) assert np.array_equal(f.trace[11], f.gather[2, 3, 2]) traces = segyio.tools.collect(f.trace[10:12]) gather = f.gather[2, 3, :] assert np.array_equal(traces, gather) assert np.array_equal(traces, f.gather[2, 3]) assert np.array_equal(empty, f.gather[2, 3, 1:0]) assert np.array_equal(empty, f.gather[2, 3, 3:4]) for g, line in zip(f.gather[1:3, 3, 1], f.iline[1:3]): assert 10 == len(g) assert (10,) == g.shape assert np.array_equal(line[2], g) for g, line in zip(f.gather[1:3, 3, :], f.iline[1:3]): assert 2 == len(g) assert (2, 10) == g.shape assert np.array_equal(line[2], g[0]) for g, line in zip(f.gather[1, 1:3, 1], f.xline[1:3]): assert 10 == len(g) assert (10,) == g.shape assert np.array_equal(line[0], g) for g, line in zip(f.gather[1, 1:3, :], f.xline[1:3]): assert 2 == len(g) assert (2, 10) == g.shape assert np.array_equal(line[0], g[0]) def test_line_generators(): with segyio.open("test-data/small.sgy") as f: for _ in f.iline: pass for _ in f.xline: pass def test_fast_slow_dimensions(): with segyio.open("test-data/small.sgy", 'r') as f: for iline, fline in zip(f.iline, f.fast): assert np.array_equal(iline, fline) for xline, sline in zip(f.xline, f.slow): assert np.array_equal(xline, sline) def test_traces_raw(): with segyio.open("test-data/small.sgy") as f: gen_traces = np.array(list(map(np.copy, f.trace)), dtype=np.single) raw_traces = f.trace.raw[:] assert np.array_equal(gen_traces, raw_traces) assert len(gen_traces) == f.tracecount assert len(raw_traces) == f.tracecount assert gen_traces[0][49] == raw_traces[0][49] assert gen_traces[1][49] == f.trace.raw[1][49] assert gen_traces[2][49] == raw_traces[2][49] assert np.array_equal(f.trace[10], f.trace.raw[10]) for raw, gen in zip(f.trace.raw[::2], f.trace[::2]): assert np.array_equal(raw, gen) for raw, gen in zip(f.trace.raw[::-1], f.trace[::-1]): assert np.array_equal(raw, gen) def test_read_header(): with segyio.open("test-data/small.sgy") as f: assert 1 == f.header[0][189] assert 1 == f.header[1][TraceField.INLINE_3D] assert 1 == f.header[1][segyio.su.iline] assert 5 == f.header[-1][segyio.su.iline] assert 5 == f.header[24][segyio.su.iline] assert dict(f.header[-1]) == dict(f.header[24]) with pytest.raises(IndexError): _ = f.header[30] with pytest.raises(IndexError): _ = f.header[-30] with pytest.raises(IndexError): _ = f.header[0][188] # between byte offsets with pytest.raises(IndexError): _ = f.header[0][-1] with pytest.raises(IndexError): _ = f.header[0][700] @tmpfiles("test-data/small.sgy") def test_write_header(tmpdir): with segyio.open(tmpdir / "small.sgy", "r+") as f: # assign to a field in a header, write immediately f.header[0][189] = 42 f.flush() assert 42 == f.header[0][189] assert 1 == f.header[1][189] # accessing non-existing offsets raises exceptions with pytest.raises(IndexError): f.header[0][188] = 1 # between byte offsets with pytest.raises(IndexError): f.header[0][-1] = 1 with pytest.raises(IndexError): f.header[0][700] = 1 d = {TraceField.INLINE_3D: 43, TraceField.CROSSLINE_3D: 11, TraceField.offset: 15} # assign multiple fields at once by using a dict f.header[1] = d f.flush() assert 43 == f.header[1][TraceField.INLINE_3D] assert 11 == f.header[1][segyio.su.xline] assert 15 == f.header[1][segyio.su.offset] # looking up multiple values at once returns a { TraceField: value } dict assert d == f.header[1][TraceField.INLINE_3D, TraceField.CROSSLINE_3D, TraceField.offset] # slice-support over headers (similar to trace) for _ in f.header[0:10]: pass assert 6 == len(list(f.header[10::-2])) assert 5 == len(list(f.header[10:5:-1])) assert 0 == len(list(f.header[10:5])) d = {TraceField.INLINE_3D: 45, TraceField.CROSSLINE_3D: 10, TraceField.offset: 16} # assign multiple values using alternative syntax f.header[5].update(d) f.flush() assert 45 == f.header[5][TraceField.INLINE_3D] assert 10 == f.header[5][segyio.su.xline] assert 16 == f.header[5][segyio.su.offset] # accept anything with a key-value structure f.header[5].update([(segyio.su.ns, 12), (segyio.su.dt, 4)]) f.header[5].update(((segyio.su.muts, 3), (segyio.su.mute, 7))) with pytest.raises(TypeError): f.header[0].update(10) with pytest.raises(TypeError): f.header[0].update(None) with pytest.raises(ValueError): f.header[0].update('foo') f.flush() assert 12 == f.header[5][segyio.su.ns] assert 4 == f.header[5][segyio.su.dt] assert 3 == f.header[5][segyio.su.muts] assert 7 == f.header[5][segyio.su.mute] # for-each support for _ in f.header: pass # copy a header f.header[2] = f.header[1] f.flush() # don't use this interface in production code, it's only for testing # i.e. don't access buf of treat it as a list # assertEqual(list(f.header[2].buf), list(f.header[1].buf)) @tmpfiles("test-data/small.sgy") def test_write_binary(tmpdir): with segyio.open(tmpdir / "small.sgy", "r+") as f: f.bin[3213] = 5 f.flush() assert 5 == f.bin[3213] # accessing non-existing offsets raises exceptions with pytest.raises(IndexError): _ = f.bin[0] with pytest.raises(IndexError): _ = f.bin[50000] with pytest.raises(IndexError): _ = f.bin[3214] d = {BinField.Traces: 43, BinField.SweepFrequencyStart: 11} # assign multiple fields at once by using a dict f.bin = d f.flush() assert 43 == f.bin[segyio.su.ntrpr] assert 11 == f.bin[segyio.su.hsfs] d = {BinField.Traces: 45, BinField.SweepFrequencyStart: 10} # assign multiple values using alternative syntax f.bin.update(d) f.flush() assert 45 == f.bin[segyio.su.ntrpr] assert 10 == f.bin[segyio.su.hsfs] # accept anything with a key-value structure f.bin.update([(segyio.su.jobid, 12), (segyio.su.lino, 4)]) f.bin.update(((segyio.su.reno, 3), (segyio.su.hdt, 7))) f.flush() assert 12 == f.bin[segyio.su.jobid] assert 4 == f.bin[segyio.su.lino] assert 3 == f.bin[segyio.su.reno] assert 7 == f.bin[segyio.su.hdt] # looking up multiple values at once returns a { TraceField: value } dict assert d == f.bin[BinField.Traces, BinField.SweepFrequencyStart] # copy a header f.bin = f.bin def test_fopen_error(): # non-existent file with pytest.raises(IOError): segyio.open("no_dir/no_file") # non-existant mode with pytest.raises(ValueError): segyio.open("test-data/small.sgy", "foo") with pytest.raises(ValueError): segyio.open("test-data/small.sgy", "r+b+toolong") def test_wrong_lineno(): with pytest.raises(KeyError): with segyio.open("test-data/small.sgy") as f: _ = f.iline[3000] with pytest.raises(KeyError): with segyio.open("test-data/small.sgy") as f: _ = f.xline[2] def test_open_wrong_inline(): with pytest.raises(IndexError): with segyio.open("test-data/small.sgy", "r", 2): pass with segyio.open("test-data/small.sgy", "r", 2, strict=False): pass def test_open_wrong_crossline(): with pytest.raises(IndexError): with segyio.open("test-data/small.sgy", "r", 189, 2): pass with segyio.open("test-data/small.sgy", "r", 189, 2, strict=False): pass def test_wonky_dimensions(): with segyio.open("test-data/Mx1.sgy"): pass with segyio.open("test-data/1xN.sgy"): pass with segyio.open("test-data/1x1.sgy"): pass def test_open_fails_unstructured(): with segyio.open("test-data/small.sgy", "r", 37, strict=False) as f: with pytest.raises(ValueError): _ = f.iline[10] with pytest.raises(ValueError): _ = f.iline[:, :] with pytest.raises(ValueError): _ = f.xline[:, :] with pytest.raises(ValueError): _ = f.depth_slice[2] # operations that don't rely on geometry still works assert f.header[2][189] == 1 assert (list(f.attributes(189)[:]) == [(i // 5) + 1 for i in range(len(f.trace))]) @tmpfiles("test-data/small.sgy") def test_assign_all_traces(tmpdir): shutil.copy(tmpdir / 'small.sgy', tmpdir / 'copy_small.sgy') with segyio.open(tmpdir / 'small.sgy') as f: traces = f.trace.raw[:] * 2.0 with segyio.open(tmpdir / 'copy_small.sgy', 'r+') as f: f.trace[:] = traces[:] with segyio.open(tmpdir / 'copy_small.sgy') as f: assert np.array_equal(f.trace.raw[:], traces) def test_traceaccess_from_array(): a = np.arange(10, dtype=np.int) b = np.arange(10, dtype=np.int32) c = np.arange(10, dtype=np.int64) d = np.arange(10, dtype=np.intc) with segyio.open("test-data/small.sgy") as f: _ = f.trace[a[0]] _ = f.trace[b[1]] _ = f.trace[c[2]] _ = f.trace[d[3]] @tmpfiles("test-data/small.sgy") def test_create_sgy(tmpdir): with segyio.open(tmpdir / "small.sgy") as src: spec = segyio.spec() spec.format = int(src.format) spec.sorting = int(src.sorting) spec.samples = src.samples spec.ilines = src.ilines spec.xlines = src.xlines with segyio.create(tmpdir / "small_created.sgy", spec) as dst: dst.text[0] = src.text[0] dst.bin = src.bin # copy all headers dst.header = src.header for i, srctr in enumerate(src.trace): dst.trace[i] = srctr dst.trace = src.trace # this doesn't work yet, some restructuring is necessary # if it turns out to be a desired feature it's rather easy to do # for dsth, srch in zip(dst.header, src.header): # dsth = srch # for dsttr, srctr in zip(dst.trace, src.trace): # dsttr = srctr assert filecmp.cmp(tmpdir / "small.sgy", tmpdir / "small_created.sgy") @tmpfiles("test-data/small.sgy") def test_create_sgy_truncate(tmpdir): with segyio.open(tmpdir / "small.sgy") as src: spec = segyio.tools.metadata(src) # repeat the text header 3 times text = src.text[0] text = text + text + text with segyio.create(tmpdir / "text-truncated.sgy", spec) as dst: dst.bin = src.bin dst.text[0] = text dst.header = src.header for i, srctr in enumerate(src.trace): dst.trace[i] = srctr dst.trace = src.trace assert filecmp.cmp(tmpdir / "small.sgy", tmpdir / "text-truncated.sgy") @tmpfiles("test-data/small.sgy") def test_create_sgy_shorter_traces(tmpdir): with segyio.open(tmpdir / "small.sgy") as src: spec = segyio.spec() spec.format = int(src.format) spec.sorting = int(src.sorting) spec.samples = src.samples[:20] # reduces samples per trace spec.ilines = src.ilines spec.xlines = src.xlines with segyio.create(tmpdir / "small_created_shorter.sgy", spec) as dst: for i, srch in enumerate(src.header): dst.header[i] = srch d = {TraceField.INLINE_3D: srch[TraceField.INLINE_3D] + 100} dst.header[i] = d for lineno in dst.ilines: dst.iline[lineno] = src.iline[lineno] # alternative form using left-hand-side slices dst.iline[2:4] = src.iline for lineno in dst.xlines: dst.xline[lineno] = src.xline[lineno] with segyio.open(tmpdir / "small_created_shorter.sgy") as dst: assert 20 == len(dst.samples) assert [x + 100 for x in src.ilines] == list(dst.ilines) def test_create_from_naught(tmpdir): spec = segyio.spec() spec.format = 5 spec.sorting = 2 spec.samples = range(150) spec.ilines = range(1, 11) spec.xlines = range(1, 6) with segyio.create(tmpdir / "mk.sgy", spec) as dst: tr = np.arange(start=1.000, stop=1.151, step=0.001, dtype=np.single) for i in range(len(dst.trace)): dst.trace[i] = tr tr += 1.000 for il in spec.ilines: dst.header.iline[il] = {TraceField.INLINE_3D: il} for xl in spec.xlines: dst.header.xline[xl] = {TraceField.CROSSLINE_3D: xl} # Set header field 'offset' to 1 in all headers dst.header = {TraceField.offset: 1} with segyio.open(tmpdir / "mk.sgy") as f: assert 1, approx(f.trace[0][0], abs=1e-4) assert 1.001, approx(f.trace[0][1], abs=1e-4) assert 1.149, approx(f.trace[0][-1], abs=1e-4) assert 50.100, approx(f.trace[-1][100], abs=1e-4) assert f.header[0][TraceField.offset] == f.header[1][TraceField.offset] assert 1 == f.header[1][TraceField.offset] def test_create_from_naught_prestack(tmpdir): spec = segyio.spec() spec.format = 5 spec.sorting = 2 spec.samples = range(7) spec.ilines = range(1, 4) spec.xlines = range(1, 3) spec.offsets = range(1, 6) with segyio.create(tmpdir / "mk-ps.sgy", spec) as dst: arr = np.arange(start=0.000, stop=0.007, step=0.001, dtype=np.single) arr = np.concatenate([[arr + 0.01], [arr + 0.02]], axis=0) lines = [arr + i for i in spec.ilines] cube = [(off * 100) + line for line in lines for off in spec.offsets] dst.iline[:, :] = cube for of in spec.offsets: for il in spec.ilines: dst.header.iline[il, of] = {TraceField.INLINE_3D: il, TraceField.offset: of } for xl in spec.xlines: dst.header.xline[xl, of] = {TraceField.CROSSLINE_3D: xl} with segyio.open(tmpdir / "mk-ps.sgy") as f: assert 101.010, approx(f.trace[0][0], abs=1e-4) assert 101.011, approx(f.trace[0][1], abs=1e-4) assert 101.016, approx(f.trace[0][-1], abs=1e-4) assert 503.025, approx(f.trace[-1][5], abs=1e-4) assert f.header[0][TraceField.offset] != f.header[1][TraceField.offset] assert 1 == f.header[0][TraceField.offset] assert 2 == f.header[1][TraceField.offset] for x, y in zip(f.iline[:, :], cube): assert list(x.flatten()) == list(y.flatten()) def test_create_write_lines(tmpdir): mklines(tmpdir / "mklines.sgy") with segyio.open(tmpdir / "mklines.sgy") as f: assert 1, approx(f.iline[1][0][0], abs=1e-4) assert 2.004, approx(f.iline[2][0][4], abs=1e-4) assert 2.014, approx(f.iline[2][1][4], abs=1e-4) assert 8.043, approx(f.iline[8][4][3], abs=1e-4) def test_create_sgy_skip_lines(tmpdir): mklines(tmpdir / "lines.sgy") with segyio.open(tmpdir / "lines.sgy") as src: spec = segyio.spec() spec.format = int(src.format) spec.sorting = int(src.sorting) spec.samples = src.samples spec.ilines = src.ilines[::2] spec.xlines = src.xlines[::2] with segyio.create(tmpdir / "lines-halved.sgy", spec) as dst: # use the inline headers as base dst.header.iline = src.header.iline[::2] # then update crossline numbers from the crossline headers for xl in dst.xlines: f = next(src.header.xline[xl])[TraceField.CROSSLINE_3D] dst.header.xline[xl] = {TraceField.CROSSLINE_3D: f} # but we override the last xline to be 6, not 5 dst.header.xline[5] = {TraceField.CROSSLINE_3D: 6} dst.iline = src.iline[::2] with segyio.open(tmpdir / "lines-halved.sgy") as f: assert list(f.ilines) == list(spec.ilines) assert list(f.xlines) == [1, 3, 6] assert 1 == approx(f.iline[1][0][0], abs=1e-4) assert 3.004 == approx(f.iline[3][0][4], abs=1e-4) assert 3.014 == approx(f.iline[3][1][4], abs=1e-4) assert 7.023 == approx(f.iline[7][2][3], abs=1e-4) def mklines(fname): spec = segyio.spec() spec.format = 5 spec.sorting = 2 spec.samples = range(10) spec.ilines = range(1, 11) spec.xlines = range(1, 6) # create a file with 10 inlines, with values on the form l.0tv where # l = line no # t = trace number (within line) # v = trace value # i.e. 2.043 is the value at inline 2's fourth trace's third value with segyio.create(fname, spec) as dst: ln = np.arange(start=0, stop=0.001 * (5 * 10), step=0.001, dtype=np.single).reshape(5, 10) for il in spec.ilines: ln += 1 dst.header.iline[il] = {TraceField.INLINE_3D: il} dst.iline[il] = ln for xl in spec.xlines: dst.header.xline[xl] = {TraceField.CROSSLINE_3D: xl} def test_create_bad_specs(): class C: pass c = C() mandatory = [('iline', 189), ('xline', 193), ('samples', [10, 11, 12]), ('format', 1), ('t0', 10.2)] for attr, val in mandatory: setattr(c, attr, val) with pytest.raises(AttributeError): with segyio.create("foo", c): pass c.tracecount = 10 with segyio.create("foo", c): pass del c.tracecount c.ilines = [1, 2, 3] with pytest.raises(AttributeError): with segyio.create("foo", c): pass c.xlines = [4, 6, 8] with pytest.raises(AttributeError): with segyio.create("foo", c): pass c.offsets = [1] with pytest.raises(AttributeError): with segyio.create("foo", c): pass c.sorting = 2 with segyio.create("foo", c): pass def test_segyio_types(): with segyio.open("test-data/small.sgy") as f: assert isinstance(f.sorting, int) assert isinstance(f.ext_headers, int) assert isinstance(f.tracecount, int) assert isinstance(f.samples, np.ndarray) assert isinstance(f.depth_slice, Line) assert isinstance(f.depth_slice[1], np.ndarray) assert isinstance(f.depth_slice[1:23], GeneratorType) assert isinstance(f.ilines, np.ndarray) assert isinstance(f.iline, Line) assert isinstance(f.iline[1], np.ndarray) assert isinstance(f.iline[1:3], GeneratorType) assert isinstance(f.iline[1][0], np.ndarray) assert isinstance(f.iline[1][0:2], np.ndarray) assert isinstance(float(f.iline[1][0][0]), float) assert isinstance(f.iline[1][0][0:3], np.ndarray) assert isinstance(f.xlines, np.ndarray) assert isinstance(f.xline, Line) assert isinstance(f.xline[21], np.ndarray) assert isinstance(f.xline[21:23], GeneratorType) assert isinstance(f.xline[21][0], np.ndarray) assert isinstance(f.xline[21][0:2], np.ndarray) assert isinstance(float(f.xline[21][0][0]), float) assert isinstance(f.xline[21][0][0:3], np.ndarray) assert isinstance(f.header, Header) assert isinstance(f.header.iline, Line) assert isinstance(f.header.iline[1], GeneratorType) assert isinstance(next(f.header.iline[1]), Field) assert isinstance(f.header.xline, Line) assert isinstance(f.header.xline[21], GeneratorType) assert isinstance(next(f.header.xline[21]), Field) assert isinstance(f.trace, Trace) assert isinstance(f.trace[0], np.ndarray) assert isinstance(f.bin, Field) assert isinstance(f.text, object) # inner TextHeader instance def test_depth_slice_reading(): with segyio.open("test-data/small.sgy") as f: assert len(f.depth_slice) == len(f.samples) for depth_sample in range(len(f.samples)): depth_slice = f.depth_slice[depth_sample] assert isinstance(depth_slice, np.ndarray) assert depth_slice.shape == (5, 5) for x, y in itertools.product(f.ilines, f.xlines): i, j = x - f.ilines[0], y - f.xlines[0] assert depth_slice[i][j] == approx(f.iline[x][j][depth_sample], abs=1e-6) for index, depth_slice in enumerate(f.depth_slice): assert isinstance(depth_slice, np.ndarray) assert depth_slice.shape == (5, 5) for x, y in itertools.product(f.ilines, f.xlines): i, j = x - f.ilines[0], y - f.xlines[0] assert depth_slice[i][j] == approx(f.iline[x][j][index], abs=1e-6) with pytest.raises(KeyError): _ = f.depth_slice[len(f.samples)] @tmpfiles("test-data/small.sgy") def test_depth_slice_writing(tmpdir): buf = np.empty(shape=(5, 5), dtype=np.single) def value(x, y): return x + (1.0 // 5) * y for x, y in itertools.product(range(5), range(5)): buf[x][y] = value(x, y) with segyio.open(tmpdir / "small.sgy", "r+") as f: f.depth_slice[7] = buf * 3.14 # assign to depth 7 assert np.allclose(f.depth_slice[7], buf * 3.14) f.depth_slice = [buf * i for i in range(len(f.depth_slice))] # assign to all depths for index, depth_slice in enumerate(f.depth_slice): assert np.allclose(depth_slice, buf * index) def test_no_16bit_overflow_tracecount(tmpdir): spec = segyio.spec() spec.format = 1 spec.sorting = 2 spec.samples = np.arange(501) spec.ilines = np.arange(345) spec.xlines = np.arange(250) # build a file with more than 65k traces, which would cause a 16bit int to # overflow. # see https://github.com/Statoil/segyio/issues/235 ones = np.ones(len(spec.samples), dtype = np.single) with segyio.create(tmpdir / 'foo.sgy', spec) as f: assert f.tracecount > 0 assert f.tracecount > 2**16 - 1 for i in range(f.tracecount): f.trace[i] = ones f.header[i] = { segyio.TraceField.INLINE_3D: i, segyio.TraceField.CROSSLINE_3D: i, segyio.TraceField.offset: 1, } segyio-1.5.2/python/test/tools.py0000664000372000037200000001511413253720451016436 0ustar travistravisimport numpy as np import pytest from pytest import approx from test import tmpfiles import segyio from segyio import BinField from segyio import TraceField @tmpfiles("test-data/small.sgy") def test_dt_fallback(tmpdir): with segyio.open(tmpdir / 'small.sgy', "r+") as f: # Both zero f.bin[BinField.Interval] = 0 f.header[0][TraceField.TRACE_SAMPLE_INTERVAL] = 0 f.flush() fallback_dt = 4 assert segyio.dt(f, fallback_dt) == approx(fallback_dt) # dt in bin header different from first trace f.bin[BinField.Interval] = 6000 f.header[0][TraceField.TRACE_SAMPLE_INTERVAL] = 1000 f.flush() fallback_dt = 4 assert segyio.dt(f, fallback_dt) == approx(fallback_dt) @tmpfiles("test-data/small.sgy") def test_dt_no_fallback(tmpdir): dt_us = 6000 with segyio.open(tmpdir / 'small.sgy', "r+") as f: f.bin[BinField.Interval] = dt_us f.header[0][TraceField.TRACE_SAMPLE_INTERVAL] = dt_us f.flush() assert segyio.dt(f) == approx(dt_us) def test_sample_indexes(): with segyio.open("test-data/small.sgy") as f: indexes = segyio.sample_indexes(f) step = 4000.0 assert indexes == [t * step for t in range(len(f.samples))] indexes = segyio.sample_indexes(f, t0=1.5) assert indexes == [1.5 + t * step for t in range(len(f.samples))] indexes = segyio.sample_indexes(f, t0=1.5, dt_override=3.21) assert indexes == [1.5 + t * 3.21 for t in range(len(f.samples))] def test_empty_text_header_creation(): text_header = segyio.create_text_header({}) for line_no in range(0, 40): line = text_header[line_no * 80: (line_no + 1) * 80] assert line == "C{0:>2} {1:76}".format(line_no + 1, "") def test_wrap(): with segyio.open("test-data/small.sgy") as f: segyio.tools.wrap(f.text[0]) segyio.tools.wrap(f.text[0], 90) def test_values_text_header_creation(): lines = {i + 1: chr(64 + i) * 76 for i in range(40)} text_header = segyio.create_text_header(lines) for line_no in range(0, 40): line = text_header[line_no * 80: (line_no + 1) * 80] assert line == "C{0:>2} {1:76}".format(line_no + 1, chr(64 + line_no) * 76) def test_native(): with open("test-data/small.sgy", 'rb') as f, segyio.open("test-data/small.sgy") as sgy: f.read(3600 + 240) filetr = f.read(4 * len(sgy.samples)) segytr = sgy.trace[0] filetr = np.frombuffer(filetr, dtype=np.single) assert not np.array_equal(segytr, filetr) assert np.array_equal(segytr, segyio.tools.native(filetr)) def test_cube_filename(): with segyio.open("test-data/small.sgy") as f: c1 = segyio.tools.cube(f) c2 = segyio.tools.cube("test-data/small.sgy") assert np.all(c1 == c2) def test_cube_identity(): with segyio.open("test-data/small.sgy") as f: x = segyio.tools.collect(f.trace[:]) x = x.reshape((len(f.ilines), len(f.xlines), len(f.samples))) assert np.all(x == segyio.tools.cube(f)) def test_cube_identity_prestack(): with segyio.open("test-data/small-ps.sgy") as f: dims = (len(f.ilines), len(f.xlines), len(f.offsets), len(f.samples)) x = segyio.tools.collect(f.trace[:]).reshape(dims) assert np.all(x == segyio.tools.cube(f)) def test_unstructured_rotation(): with pytest.raises(ValueError): with segyio.open("test-data/small.sgy", ignore_geometry=True) as f: segyio.tools.rotation(f) def test_rotation(): names = ['normal', 'acute', 'right', 'obtuse', 'straight', 'reflex', 'left', 'inv-acute'] angles = [0.000, 0.785, 1.571, 2.356, 3.142, 3.927, 4.712, 5.498] rights = [1.571, 2.356, 3.142, 3.927, 4.712, 5.498, 0.000, 0.785] def rotation(x, **kwargs): return segyio.tools.rotation(x, **kwargs)[0] for name, angle, right in zip(names, angles, rights): src = "test-data/small.sgy".replace('/', '/' + name + '-') with segyio.open(src) as f: assert angle == approx(rotation(f), abs=1e-3) assert angle == approx(rotation(f, line='fast'), abs=1e-3) assert angle == approx(rotation(f, line='iline'), abs=1e-3) assert right == approx(rotation(f, line='slow'), abs=1e-3) assert right == approx(rotation(f, line='xline'), abs=1e-3) def test_metadata(): spec = segyio.spec() spec.ilines = [1, 2, 3, 4, 5] spec.xlines = [20, 21, 22, 23, 24] spec.samples = list(range(0, 200, 4)) spec.sorting = 2 spec.format = 1 smallspec = segyio.tools.metadata("test-data/small.sgy") assert np.array_equal(spec.ilines, smallspec.ilines) assert np.array_equal(spec.xlines, smallspec.xlines) assert np.array_equal(spec.offsets, smallspec.offsets) assert np.array_equal(spec.samples, smallspec.samples) assert spec.sorting == smallspec.sorting assert spec.format == int(smallspec.format) @tmpfiles("test-data/small.sgy") def test_resample_none(tmpdir): old = list(range(0, 200, 4)) with segyio.open(tmpdir / 'small.sgy', 'r+') as f: assert np.array_equal(old, f.samples) segyio.tools.resample(f) # essentially a no-op assert np.array_equal(old, f.samples) with segyio.open(tmpdir / 'small.sgy') as f: assert np.array_equal(old, f.samples) @tmpfiles("test-data/small.sgy") def test_resample_all(tmpdir): old = list(range(0, 200, 4)) new = list(range(12, 112, 2)) with segyio.open(tmpdir / 'small.sgy', 'r+') as f: assert np.array_equal(old, f.samples) segyio.tools.resample(f, rate=2, delay=12) assert np.array_equal(new, f.samples) with segyio.open(tmpdir / 'small.sgy') as f: assert np.array_equal(new, f.samples) @tmpfiles("test-data/small.sgy") def test_resample_rate(tmpdir): old = list(range(0, 200, 4)) new = list(range(12, 212, 4)) with segyio.open(tmpdir / 'small.sgy', 'r+') as f: assert np.array_equal(old, f.samples) segyio.tools.resample(f, delay=12) assert np.array_equal(new, f.samples) with segyio.open(tmpdir / 'small.sgy') as f: assert np.array_equal(new, f.samples) @tmpfiles("test-data/small.sgy") def test_resample_delay(tmpdir): old = list(range(0, 200, 4)) new = list(range(0, 100, 2)) with segyio.open(tmpdir / 'small.sgy', 'r+') as f: assert np.array_equal(old, f.samples) segyio.tools.resample(f, rate=2000, micro=True) assert np.array_equal(new, f.samples) with segyio.open(tmpdir / 'small.sgy') as f: assert np.array_equal(new, f.samples) segyio-1.5.2/python/test/__init__.py0000664000372000037200000000035413253720451017035 0ustar travistravisimport shutil def tmpfiles(*files): def tmpfiles_decorator(func): def func_wrapper(tmpdir): for f in files: shutil.copy(f, str(tmpdir)) return func_wrapper return tmpfiles_decorator segyio-1.5.2/python/test/segyioenum.py0000664000372000037200000000057613253720451017470 0ustar travistravisfrom segyio import Enum def test_enum(): class TestEnum(Enum): ZERO = 0 ONE = 1 TWO = 2 assert TestEnum.ONE == 1 one = TestEnum(TestEnum.ONE) assert isinstance(one, TestEnum) assert str(one) == "ONE" assert int(one) == 1 assert one == 1 assert TestEnum.enums() == TestEnum.enums() assert TestEnum.enums() == [0, 1, 2] segyio-1.5.2/python/test/segyio_c.py0000664000372000037200000003427513253720451017110 0ustar travistravisfrom __future__ import absolute_import import os import numpy import pytest from pytest import approx from test import tmpfiles import segyio import segyio._segyio as _segyio def test_binary_header_size(): assert 400 == _segyio.binsize() def test_textheader_size(): assert 3200 == _segyio.textsize() def test_open_non_existing_file(): with pytest.raises(IOError): _ = _segyio.segyiofd("non-existing", "r") def test_close_non_existing_file(): with pytest.raises(TypeError): _segyio.segyiofd.close(None) def test_open_and_close_file(): f = _segyio.segyiofd("test-data/small.sgy", "r") f.close() def test_open_flush_and_close_file(): f = _segyio.segyiofd("test-data/small.sgy", "r") f.flush() f.close() with pytest.raises(IOError): f.flush() def test_read_text_header_mmap(): test_read_text_header(True) def test_read_text_header(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() lines = { 1: "DATE: 2016-09-19", 2: "AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE", 3: "Written by libsegyio (python)", 11: "TRACE HEADER POSITION:", 12: " INLINE BYTES 189-193 | OFFSET BYTES 037-041", 13: " CROSSLINE BYTES 193-197 |", 15: "END EBCDIC HEADER" } rows = segyio.create_text_header(lines) rows = bytearray(rows, 'ascii') # mutable array of bytes rows[-1] = 128 # \x80 actual_text_header = bytes(rows) assert f.gettext(0) == actual_text_header with pytest.raises(Exception): _segyio.read_texthdr(None, 0) f.close() @tmpfiles("test-data/small.sgy") def test_write_text_header_mmap(tmpdir): f = get_instance_segyiofd(tmpdir) write_text_header(f, True) @tmpfiles("test-data/small.sgy") def test_write_text_header(tmpdir): f = get_instance_segyiofd(tmpdir) write_text_header(f, False) def write_text_header(f, mmap): if mmap: f.mmap() f.puttext(0, "") textheader = f.gettext(0) textheader = textheader.decode('ascii') assert textheader == "" * 3200 f.puttext(0, "yolo" * 800) textheader = f.gettext(0) textheader = textheader.decode('ascii') # Because in Python 3.5 bytes are not comparable to strings assert textheader == "yolo" * 800 f.close() def get_instance_segyiofd(tmpdir, file_name="small.sgy", mode="r+", tracecount=0, binary=None): path = str(tmpdir) f = os.path.join(path, file_name) if binary is not None: return _segyio.segyiofd(f, mode, binary) else: return _segyio.segyiofd(f, mode) @tmpfiles("test-data/small.sgy") def test_read_and_write_binary_header(tmpdir): f = get_instance_segyiofd(tmpdir) read_and_write_binary_header(f, False) @tmpfiles("test-data/small.sgy") def test_read_and_write_binary_header_mmap(tmpdir): f = get_instance_segyiofd(tmpdir) read_and_write_binary_header(f, True) def read_and_write_binary_header(f, mmap): if mmap: f.mmap() binary_header = f.getbin() with pytest.raises(ValueError): f.putbin("Buffer too small") f.putbin(binary_header) f.close() def test_read_binary_header_fields_mmap(): test_read_binary_header_fields(True) def test_read_binary_header_fields(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() binary_header = f.getbin() with pytest.raises(TypeError): _ = _segyio.getfield([], 0) with pytest.raises(IndexError): _ = _segyio.getfield(binary_header, -1) assert _segyio.getfield(binary_header, 3225) == 1 assert _segyio.getfield(binary_header, 3221) == 50 f.close() def test_line_metrics_mmap(): test_line_metrics(True) def test_line_metrics(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() ilb = 189 xlb = 193 metrics = f.metrics() metrics.update(f.cube_metrics(ilb, xlb)) f.close() sorting = metrics['sorting'] trace_count = metrics['tracecount'] inline_count = metrics['iline_count'] crossline_count = metrics['xline_count'] offset_count = metrics['offset_count'] metrics = _segyio.line_metrics(sorting, trace_count, inline_count, crossline_count, offset_count) assert metrics['xline_length'] == 5 assert metrics['xline_stride'] == 5 assert metrics['iline_length'] == 5 assert metrics['iline_stride'] == 1 # (sorting, trace_count, inline_count, crossline_count, offset_count) metrics = _segyio.line_metrics(1, 15, 3, 5, 1) assert metrics['xline_length'] == 3 assert metrics['xline_stride'] == 1 assert metrics['iline_length'] == 5 assert metrics['iline_stride'] == 3 metrics = _segyio.line_metrics(2, 15, 3, 5, 1) assert metrics['xline_length'] == 3 assert metrics['xline_stride'] == 5 assert metrics['iline_length'] == 5 assert metrics['iline_stride'] == 1 def test_metrics_mmap(): test_metrics(True) def test_metrics(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() ilb = 189 xlb = 193 with pytest.raises(IndexError): metrics = f.metrics() metrics.update(f.cube_metrics(ilb + 1, xlb)) metrics = f.metrics() metrics.update(f.cube_metrics(ilb, xlb)) assert metrics['trace0'] == _segyio.textsize() + _segyio.binsize() assert metrics['samplecount'] == 50 assert metrics['format'] == 1 assert metrics['trace_bsize'] == 200 assert metrics['sorting'] == 2 # inline sorting = 2, crossline sorting = 1 assert metrics['tracecount'] == 25 assert metrics['offset_count'] == 1 assert metrics['iline_count'] == 5 assert metrics['xline_count'] == 5 f.close() def test_indices_mmap(): test_indices(True) def test_indices(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() ilb = 189 xlb = 193 metrics = f.metrics() dmy = numpy.zeros(2, dtype=numpy.intc) dummy_metrics = {'xline_count': 2, 'iline_count': 2, 'offset_count': 1} with pytest.raises(TypeError): f.indices("-", dmy, dmy, dmy) with pytest.raises(TypeError): f.indices(dummy_metrics, 1, dmy, dmy) with pytest.raises(TypeError): f.indices(dummy_metrics, dmy, 2, dmy) with pytest.raises(TypeError): f.indices(dummy_metrics, dmy, dmy, 2) one = numpy.zeros(1, dtype=numpy.intc) two = numpy.zeros(2, dtype=numpy.intc) off = numpy.zeros(1, dtype=numpy.intc) with pytest.raises(ValueError): f.indices(dummy_metrics, one, two, off) with pytest.raises(ValueError): f.indices(dummy_metrics, two, one, off) metrics.update(f.cube_metrics(ilb, xlb)) # Happy Path iline_indexes = numpy.zeros(metrics['iline_count'], dtype=numpy.intc) xline_indexes = numpy.zeros(metrics['xline_count'], dtype=numpy.intc) offsets = numpy.zeros(metrics['offset_count'], dtype=numpy.intc) f.indices(metrics, iline_indexes, xline_indexes, offsets) assert [1, 2, 3, 4, 5] == list(iline_indexes) assert [20, 21, 22, 23, 24] == list(xline_indexes) assert [1] == list(offsets) f.close() def test_fread_trace0_mmap(): test_fread_trace0(True) def test_fread_trace0(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() ilb = 189 xlb = 193 metrics = f.metrics() metrics.update(f.cube_metrics(ilb, xlb)) sorting = metrics['sorting'] trace_count = metrics['tracecount'] inline_count = metrics['iline_count'] crossline_count = metrics['xline_count'] offset_count = metrics['offset_count'] line_metrics = _segyio.line_metrics(sorting, trace_count, inline_count, crossline_count, offset_count) iline_indexes = numpy.zeros(metrics['iline_count'], dtype=numpy.intc) xline_indexes = numpy.zeros(metrics['xline_count'], dtype=numpy.intc) offsets = numpy.zeros(metrics['offset_count'], dtype=numpy.intc) f.indices(metrics, iline_indexes, xline_indexes, offsets) with pytest.raises(KeyError): _segyio.fread_trace0(0, len(xline_indexes), line_metrics['iline_stride'], offset_count, iline_indexes, "inline") with pytest.raises(KeyError): _segyio.fread_trace0(2, len(iline_indexes), line_metrics['xline_stride'], offset_count, xline_indexes, "crossline") value = _segyio.fread_trace0(1, len(xline_indexes), line_metrics['iline_stride'], offset_count, iline_indexes, "inline") assert value == 0 value = _segyio.fread_trace0(2, len(xline_indexes), line_metrics['iline_stride'], offset_count, iline_indexes, "inline") assert value == 5 value = _segyio.fread_trace0(21, len(iline_indexes), line_metrics['xline_stride'], offset_count, xline_indexes, "crossline") assert value == 1 value = _segyio.fread_trace0(22, len(iline_indexes), line_metrics['xline_stride'], offset_count, xline_indexes, "crossline") assert value == 2 f.close() def test_get_and_putfield(): hdr = bytearray(_segyio.thsize()) with pytest.raises(BufferError): _segyio.getfield(".", 0) with pytest.raises(TypeError): _segyio.getfield([], 0) with pytest.raises(TypeError): _segyio.putfield({}, 0, 1) with pytest.raises(IndexError): _segyio.getfield(hdr, 0) with pytest.raises(IndexError): _segyio.putfield(hdr, 0, 1) _segyio.putfield(hdr, 1, 127) _segyio.putfield(hdr, 5, 67) _segyio.putfield(hdr, 9, 19) assert _segyio.getfield(hdr, 1) == 127 assert _segyio.getfield(hdr, 5) == 67 assert _segyio.getfield(hdr, 9) == 19 @tmpfiles("test-data/small.sgy") def test_read_and_write_traceheader_mmap(tmpdir): f = get_instance_segyiofd(tmpdir) read_and_write_traceheader(f, True) @tmpfiles("test-data/small.sgy") def test_read_and_write_traceheader(tmpdir): f = get_instance_segyiofd(tmpdir) read_and_write_traceheader(f, False) def read_and_write_traceheader(f, mmap): if mmap: f.mmap() ilb = 189 xlb = 193 def mkempty(): return bytearray(_segyio.thsize()) with pytest.raises(TypeError): f.getth("+") with pytest.raises(TypeError): f.getth(0, None) trace_header = f.getth(0, mkempty()) assert _segyio.getfield(trace_header, ilb) == 1 assert _segyio.getfield(trace_header, xlb) == 20 trace_header = f.getth(1, mkempty()) assert _segyio.getfield(trace_header, ilb) == 1 assert _segyio.getfield(trace_header, xlb) == 21 _segyio.putfield(trace_header, ilb, 99) _segyio.putfield(trace_header, xlb, 42) f.putth(0, trace_header) trace_header = f.getth(0, mkempty()) assert _segyio.getfield(trace_header, ilb) == 99 assert _segyio.getfield(trace_header, xlb) == 42 f.close() @tmpfiles("test-data/small.sgy") def test_read_and_write_trace_mmap(tmpdir): binary = bytearray(_segyio.binsize()) _segyio.putfield(binary, 3221, 25) f = get_instance_segyiofd(tmpdir, "trace-wrt.sgy", "w+", tracecount=100, binary=binary) read_and_write_trace(f, True) @tmpfiles("test-data/small.sgy") def test_read_and_write_trace(tmpdir): binary = bytearray(_segyio.binsize()) _segyio.putfield(binary, 3221, 25) f = get_instance_segyiofd(tmpdir, "trace-wrt.sgy", "w+", tracecount=100, binary=binary) read_and_write_trace(f, False) def read_and_write_trace(f, mmap): if mmap: f.mmap() buf = numpy.ones(25, dtype=numpy.single) buf[11] = 3.1415 f.puttr(0, buf) buf[:] = 42.0 f.puttr(1, buf) f.flush() buf = numpy.zeros(25, dtype=numpy.single) f.gettr(buf, 0, 1, 1) assert buf[10] == approx(1.0, abs=1e-4) assert buf[11] == approx(3.1415, abs=1e-4) f.gettr(buf, 1, 1, 1) assert sum(buf) == approx(42.0 * 25, abs=1e-4) f.close() def test_read_line(): f, metrics, iline_idx, xline_idx = read_small(False) read_line(f, metrics, iline_idx, xline_idx) def test_read_line_mmap(): f, metrics, iline_idx, xline_idx = read_small(True) read_line(f, metrics, iline_idx, xline_idx) def read_line(f, metrics, iline_idx, xline_idx): samples = metrics['samplecount'] xline_stride = metrics['xline_stride'] iline_stride = metrics['iline_stride'] offsets = metrics['offset_count'] xline_trace0 = _segyio.fread_trace0(20, len(iline_idx), xline_stride, offsets, xline_idx, "crossline") iline_trace0 = _segyio.fread_trace0(1, len(xline_idx), iline_stride, offsets, iline_idx, "inline") buf = numpy.zeros((len(iline_idx), samples), dtype=numpy.single) f.getline(xline_trace0, len(iline_idx), xline_stride, offsets, buf) assert sum(sum(buf)) == approx(800.061169624, abs=1e-6) f.getline(iline_trace0, len(xline_idx), iline_stride, offsets, buf) assert sum(sum(buf)) == approx(305.061146736, abs=1e-6) f.close() def read_small(mmap=False): f = _segyio.segyiofd("test-data/small.sgy", "r") if mmap: f.mmap() ilb = 189 xlb = 193 metrics = f.metrics() metrics.update(f.cube_metrics(ilb, xlb)) sorting = metrics['sorting'] trace_count = metrics['tracecount'] inline_count = metrics['iline_count'] crossline_count = metrics['xline_count'] offset_count = metrics['offset_count'] line_metrics = _segyio.line_metrics(sorting, trace_count, inline_count, crossline_count, offset_count) metrics.update(line_metrics) iline_indexes = numpy.zeros(metrics['iline_count'], dtype=numpy.intc) xline_indexes = numpy.zeros(metrics['xline_count'], dtype=numpy.intc) offsets = numpy.zeros(metrics['offset_count'], dtype=numpy.intc) f.indices(metrics, iline_indexes, xline_indexes, offsets) return f, metrics, iline_indexes, xline_indexes def test_fread_trace0_for_depth(): elements = list(range(25)) indices = numpy.asarray(elements, dtype=numpy.intc) for index in indices: d = _segyio.fread_trace0(index, 1, 1, 1, indices, "depth") assert d == index with pytest.raises(KeyError): _segyio.fread_trace0(25, 1, 1, 1, indices, "depth") segyio-1.5.2/python/CMakeLists.txt0000664000372000037200000001311513253720451016504 0ustar travistravisproject(segyio-python) if (REQUIRE_PYTHON) set(BUILD_PYTHON ON) endif() if (NOT BUILD_PYTHON) return() endif() find_package(PythonInterp REQUIRED) if (NOT PYTHON_EXECUTABLE AND REQUIRE_PYTHON) message(SEND_ERROR "Could not find python executable") return() endif() if (NOT PYTHON_EXECUTABLE) message(WARNING "Could not find python - skipping python bindings") return() endif() if (PYTHON_INSTALL_LAYOUT) set(setup-install-layout --install-layout ${PYTHON_INSTALL_LAYOUT}) endif() set(python ${PYTHON_EXECUTABLE}) if (NOT WIN32) # setuptools on microsoft compilers doesn't support the --library-dir or # --build-dir flag and crashes, so only pass it on non-microsoft platforms set(setup-py-libdir build_ext --rpath $ --library-dirs $) set(install-no-rpath install_lib --build-dir build/install) set(build-no-rpath --library-dirs $ build --build-lib build/install) else () set(copy-dll-to-src ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_SOURCE_DIR}/segyio/$) endif () set(setup-py ${CMAKE_SOURCE_DIR}/setup.py) add_custom_target( segyio-python ALL COMMENT "Building python library with setup.py" SOURCES ${setup-py} DEPENDS ${setup-py} VERBATIM # copy the examples to have them runnable from a relative directory, so # that a locally-installed segyio can be imported COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/examples . # do the same to tests. running through setup.py test is *very* slow, so # invoke unittest manually from the build directory. more importantly, # setup.py test will pollute the source directory with egg info and # extensions, which is unacceptable in a cmake world COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/test test # setuptools on windows breaks spectacularly when the library isn't # available in the same directory, and build_ext --library-dirs is not # support on msvc is not supported, so we must copy out the libsegyio core # object and put it here COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND ${CMAKE_COMMAND} -E copy $ $ # on windows, copy the freshly-built dll to the source directory. this # voilates the cmake spirit (as does the version.py writing from # setuptools-scm), but there's no auditwheel like tool to help fix the # wheel, and the dll must still be bundled in order to make the package # work. it's paired with package_data in setup.py. this is necessary # because setup.py assumes all files to bundled with the package are # relative downwards and in the package itself, with poor support for # grabbing other files and adding to it later. COMMAND ${copy-dll-to-src} # install the lib in the build-dir so that the examples can load that from # current working dir COMMAND ${python} ${setup-py} ${setup-py-libdir} install_lib -d . # to maintain good make && make install behaviour, the extension is built # twice, one with rpath (for testing and build-dir-local) and one for # installation COMMAND ${python} ${setup-py} build_ext ${build-no-rpath} ) add_dependencies(segyio-python segyio-shared) # write egg_info to the build dir in order not to pollute the source directory # and install as if it was through some other distro by using single-version, # so that install won't die on possibly missing pythonpath # setup.py install doesn't respect DESTDIR, so "force" it by changing the # --root if DESTDIR is passed install(CODE " if (DEFINED ENV{DESTDIR}) get_filename_component(abs-destdir \"\$ENV{DESTDIR}\" ABSOLUTE) set(root_destdir --root=\${abs-destdir}) endif() execute_process(COMMAND ${python} ${setup-py} install_egg_info --install-dir . egg_info --egg-base . ${install-no-rpath} install --prefix=${CMAKE_INSTALL_PREFIX} --single-version-externally-managed --record installed-files ${setup-install-layout} \${root_destdir} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )") if(NOT BUILD_TESTING) return() endif() file(GLOB sgys "${testdata}/*.sgy") foreach (sgy ${sgys}) get_filename_component(fl ${sgy} NAME) configure_file(${sgy} test-data/${fl} COPYONLY) endforeach () set(pytest ${python} -m pytest -v) add_test(NAME python.unit COMMAND ${pytest} test/) configure_file(${testdata}/small.sgy test-data/write.sgy COPYONLY) add_test(NAME python.example.about COMMAND ${python} about.py test-data/small.sgy INLINE_3D CROSSLINE_3D) add_test(NAME python.example.write COMMAND ${python} write.py test-data/write.sgy) add_test(NAME python.example.makefile COMMAND ${python} make-file.py test-data/large-file.sgy 20 1 20 1 20) add_test(NAME python.example.makepsfile COMMAND ${python} make-ps-file.py test-data/small-prestack.sgy 10 1 5 1 4 1 3) add_test(NAME python.example.subcube COMMAND ${python} copy-sub-cube.py test-data/small.sgy test-data/copy.sgy) add_test(NAME python.example.rotate COMMAND ${python} make-rotated-copies.py test-data/small.sgy ex-rotate.sgy) add_test(NAME python.example.scan_min_max COMMAND ${python} scan_min_max.py test-data/small.sgy) segyio-1.5.2/python/segyio/0000775000372000037200000000000013253720451015242 5ustar travistravissegyio-1.5.2/python/segyio/create.py0000664000372000037200000001526013253720451017063 0ustar travistravisimport datetime import numpy import segyio import segyio._segyio as _segyio def default_text_header(iline, xline, offset): lines = { 1: "DATE %s" % datetime.date.today().isoformat(), 2: "AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE", 3: "Written by libsegyio (python)", 11: "TRACE HEADER POSITION:", 12: " INLINE BYTES %03d-%03d | OFFSET BYTES %03d-%03d" % (iline, iline + 4, int(offset), int(offset) + 4), 13: " CROSSLINE BYTES %03d-%03d |" % (xline, xline + 4), 15: "END EBCDIC HEADER", } rows = segyio.create_text_header(lines) rows = bytearray(rows, 'ascii') # mutable array of bytes rows[-1] = 128 # \x80 -- Unsure if this is really required... return bytes(rows) # immutable array of bytes that is compatible with strings def structured(spec): if not hasattr(spec, 'ilines' ): return False if not hasattr(spec, 'xlines' ): return False if not hasattr(spec, 'offsets'): return False if not hasattr(spec, 'sorting'): return False if spec.ilines is None: return False if spec.xlines is None: return False if spec.offsets is None: return False if spec.sorting is None: return False if not list(spec.ilines): return False if not list(spec.xlines): return False if not list(spec.offsets): return False if not int(spec.sorting): return False return True def create(filename, spec): """Create a new segy file. Since v1.1 Unstructured file creation since v1.4 Create a new segy file with the geometry and properties given by `spec`. This enables creating SEGY files from your data. The created file supports all segyio modes, but has an emphasis on writing. The spec must be complete, otherwise an exception will be raised. A default, empty spec can be created with `segyio.spec()`. Very little data is written to the file, so just calling create is not sufficient to re-read the file with segyio. Rather, every trace header and trace must be written to the file to be considered complete. Create should be used together with python's `with` statement. This ensure the data is written. Please refer to the examples. The `spec` is any object that has the following attributes: Mandatory: iline (int/segyio.BinField) xline (int/segyio.BinField) samples (array-like of int) format (int), 1 = IBM float, 5 = IEEE float Exclusive: ilines (array-like of int) xlines (array-like of int) offsets (array-like of int) sorting (int/segyio.TraceSortingFormat) OR tracecount (int) Optional: ext_headers (int) The `segyio.spec()` function will default offsets and everything in the mandatory group, except format and samples, and requires the caller to fill in *all* the fields in either of the exclusive groups. If any field is missing from the first exclusive group, and the tracecount is set, the resulting file will be considered unstructured. If the tracecount is set, and all fields of the first exclusive group are specified, the file is considered structured and the tracecount is inferred from the xlines/ilines/offsets. The offsets are defaulted to [1] by `segyio.spec()`. Args: filename (str-like): Path to file to open. spec (:obj: `spec`): Structure of the segy file. Examples: Create a file:: >>> spec = segyio.spec() >>> spec.ilines = [1, 2, 3, 4] >>> spec.xlines = [11, 12, 13] >>> spec.samples = list(range(50)) >>> spec.sorting = 2 >>> spec.format = 1 >>> with segyio.create(path, spec) as f: ... ## fill the file with data ... Copy a file, but shorten all traces by 50 samples:: >>> with segyio.open(srcpath) as src: ... spec = segyio.spec() ... spec.sorting = src.sorting ... spec.format = src.format ... spec.samples = src.samples[:len(src.samples) - 50] ... spec.ilines = src.ilines ... spec.xline = src.xlines ... with segyio.create(dstpath, spec) as dst: ... dst.text[0] = src.text[0] ... dst.bin = src.bin ... dst.header = src.header ... dst.trace = src.trace Copy a file, but shorten all traces by 50 samples (since v1.4):: >>> with segyio.open(srcpath) as src: ... spec = segyio.tools.metadata(src) ... spec.samples = spec.samples[:len(spec.samples) - 50] ... with segyio.create(dstpath, spec) as dst: ... dst.text[0] = src.text[0] ... dst.bin = src.bin ... dst.header = src.header ... dst.trace = src.trace :rtype: segyio.SegyFile """ if not structured(spec): tracecount = spec.tracecount else: tracecount = len(spec.ilines) * len(spec.xlines) * len(spec.offsets) ext_headers = spec.ext_headers if hasattr(spec, 'ext_headers') else 0 samples = numpy.asarray(spec.samples, dtype = numpy.single) binary = bytearray(_segyio.binsize()) _segyio.putfield(binary, 3217, 4000) _segyio.putfield(binary, 3221, len(samples)) _segyio.putfield(binary, 3225, int(spec.format)) _segyio.putfield(binary, 3505, int(ext_headers)) f = segyio.SegyFile(str(filename), "w+", tracecount = tracecount, binary = binary) f._il = int(spec.iline) f._xl = int(spec.xline) f._samples = samples if structured(spec): f._sorting = spec.sorting f._offsets = numpy.copy(numpy.asarray(spec.offsets, dtype = numpy.intc)) f._ilines = numpy.copy(numpy.asarray(spec.ilines, dtype=numpy.intc)) f._xlines = numpy.copy(numpy.asarray(spec.xlines, dtype=numpy.intc)) line_metrics = _segyio.line_metrics(f.sorting, tracecount, len(f.ilines), len(f.xlines), len(f.offsets)) f._iline_length = line_metrics['iline_length'] f._iline_stride = line_metrics['iline_stride'] f._xline_length = line_metrics['xline_length'] f._xline_stride = line_metrics['xline_stride'] f.text[0] = default_text_header(f._il, f._xl, segyio.TraceField.offset) f.xfd.putbin(binary) return f segyio-1.5.2/python/segyio/segy.py0000664000372000037200000011661113253720451016571 0ustar travistravis""" segy modes Welcome to segyio.segy. Here you will find references and examples for the various segy modes and how to interact with segy files. To start interacting with files, please refer to the segyio.open and segyio.create documentation, by typing `help(segyio.open)` or `help(segyio.create)`. The primary way of obtaining a file instance is calling segyio.open. When you have a file instance you can interact with it as described in this module. The explanations and examples here are meant as a quick guide and reference. You can also have a look at the example programs that are distributed with segyio which you can find in the examples directory or where your distribution installs example programs. """ import itertools try: from future_builtins import zip range = xrange except (NameError, ImportError): pass import numpy as np from segyio._header import Header from segyio._gather import Gather from segyio._line import Line from segyio._trace import Trace from segyio._field import Field import segyio._segyio as _segyio from segyio.tracesortingformat import TraceSortingFormat class SegyFile(object): _unstructured_errmsg = "File opened in unstructured mode." def __init__(self, filename, mode, iline=189, xline=193, tracecount=0, binary=None): """ Constructor, internal. """ self._filename = filename self._mode = mode self._il = iline self._xl = xline # property value holders self._ilines = None self._xlines = None self._offsets = None self._samples = None # private values self._iline_length = None self._iline_stride = None self._xline_length = None self._xline_stride = None self._trace = Trace(self) self._header = Header(self) self._iline = None self._xline = None self._gather = None self.xfd = _segyio.segyiofd(filename, mode, tracecount, binary) metrics = self.xfd.metrics() self._fmt = metrics['format'] self._tracecount = metrics['tracecount'] self._ext_headers = metrics['ext_headers'] super(SegyFile, self).__init__() def __str__(self): f = "SegyFile {}:".format(self._filename) if self.unstructured: il = " inlines: None" xl = " crosslines: None" of = " offsets: None" else: il = " inlines: {} [{}, {}]".format(len(self.ilines), self.ilines[0], self.ilines[-1]) xl = " crosslines: {} [{}, {}]".format(len(self.xlines), self.xlines[0], self.xlines[-1]) of = " offsets: {} [{}, {}]".format(len(self.offsets), self.offsets[0], self.offsets[-1]) tr = " traces: {}".format(self.tracecount) sm = " samples: {}".format(self.samples) fmt = " float representation: {}".format(self.format) props = [f, il, xl, tr, sm] if self.offsets is not None and len(self.offsets) > 1: props.append(of) props.append(fmt) return '\n'.join(props) def __repr__(self): return "SegyFile('{}', '{}', iline = {}, xline = {})".format( self._filename, self._mode, self._il, self._xl) def __enter__(self): """Internal. :rtype: segyio.segy.SegyFile """ return self def __exit__(self, type, value, traceback): """Internal.""" self.close() def flush(self): """Flush a file - write the library buffers to disk. Since v1.1 This method is mostly useful for testing. It is not necessary to call this method unless you want to observe your changes while the file is still open. The file will automatically be flushed for you if you use the `with` statement when your routine is completed. Examples: Flush:: >>> with segyio.open(path) as f: ... # write something to f ... f.flush() """ self.xfd.flush() def close(self): """Close the file. Since v1.1 This method is mostly useful for testing. It is not necessary to call this method if you're using the `with` statement, which will close the file for you. """ self.xfd.close() def mmap(self): """Memory map the file :rtype: bool Since v1.1 Memory map the file. This is an advanced feature for speed and optimization; however, it is no silver bullet. If your file is smaller than the memory available on your system this will likely result in faster reads and writes, especially for line modes. However, if the file is very large, or memory is very pressured, this optimization might cause overall system slowdowns. However, if you're opening the same file from many different instances of segyio then memory mapping may significantly reduce the memory pressure. If this call returns true, the file is memory mapped. If memory mapping was build-time disabled or is not available for your platform this call always return false. If the memory mapping is unsuccessful you can keep using segyio - reading and writing falls back on non-memory mapped features. Examples: Memory map:: >>> mapped = f.mmap() >>> if mapped: print( "File is memory mapped!" ) >>> # keep using segyio as per usual >>> print( f.trace[10] ) """ return self.xfd.mmap() @property def sorting(self): """ :rtype: int """ return self._sorting @property def tracecount(self): """ :rtype: int """ return self._tracecount @property def samples(self): """ :rtype: numpy.ndarray """ return self._samples @property def offsets(self): """ :rtype: numpy.ndarray""" return self._offsets @property def ext_headers(self): """ :rtype: int """ return self._ext_headers @property def unstructured(self): return self.ilines is None @property def header(self): """ Interact with segy in header mode. Since v1.1 This mode gives access to reading and writing functionality of headers, both in individual (trace) mode and line mode. Individual headers are accessed via generators and are not read from or written to disk until the generator is realised and the header in question is used. Supports python slicing (which yields a generator), as well as direct lookup and common dict operations. The header can be considered a dictionary with a constant set of keys. Examples: Reading a field in a trace:: >>> import segyio >>> f = segyio.open("filename") >>> f.header[10][TraceField.offset] Writing a field in a trace:: >>> f.header[10][TraceField.offset] = 5 Copy a header from another header:: >>> f.header[28] = f.header[29] Reading multiple fields in a trace. If raw, numerical offsets are used they must align with the defined byte offsets by the SEGY specification:: >>> f.header[10][TraceField.offset, TraceField.INLINE_3D] >>> f.header[10][37, 189] Write multiple fields in a trace:: >>> f.header[10] = { 37: 5, TraceField.INLINE_3D: 2484 } Iterate over headers and gather line numbers:: >>> [h[TraceField.INLINE_3D] for h in f.header] >>> [h[25, 189] for h in f.header] Write field in all headers:: >>> for h in f.header: ... h[37] = 1 ... h = { TraceField.offset: 1, 2484: 10 } ... Read a field in 10 first headers:: >>> [h[25] for h in f.header[0:10]] Read a field in every other header:: >>> [h[37] for h in f.header[::2]] Write a field in every other header:: >>> for h in f.header[::2]: ... h = { TraceField.offset : 2 } ... Cache a header: >>> h = f.header[12] >>> x = foo() >>> h[37] = x A convenient way for operating on all headers of a file is to use the default full-file range. It will write headers 0, 1, ..., n, but uses the iteration specified by the right-hand side (i.e. can skip headers etc). If the right-hand-side headers are exhausted before all the destination file headers the writing will stop, i.e. not all all headers in the destination file will be written to. Copy headers from file g to file f: >>> f = segyio.open("path to file") >>> g = segyio.open("path to another file") >>> f.header = g.header Set offset field:: >>> f.header = { TraceField.offset: 5 } Copy every 12th header from the file g to f's 0, 1, 2...:: >>> f.header = g.header[::12] >>> f.header[0] == g.header[0] True >>> f.header[1] == g.header[12] True >>> f.header[2] == g.header[2] False >>> f.header[2] == g.header[24] True The header mode can also be accessed with line addressing, which supports all of iline and xline's indexing features. Rename the iline 3 to 4:: >>> f.header.iline[3][TraceField.INLINE_3D] = 4 >>> # please note that rewriting the header won't update the >>> # file's interpretation of the file until you reload it, so >>> # the new iline 4 will be considered iline 3 until the file >>> # is reloaded Set offset line 3 offset 3 to 5:: >>> f.header.iline[3, 3] = { TraceField.offset: 5 } Since v1.3, common dict operations are supported. Get a list of keys and values:: >>> f.header[10].keys() >>> f.header[10].values() Get a list of key-value pairs:: >>> f.header[10].items() Get the number of keys-value pairs in a header:: >>> len(f.header[10]) Update a set of values:: >>> d = { segyio.su.tracl: 10, segyio.su.nhs: 5 } >>> f.header[10].update(d) >>> l = [ (segyio.su.sy, 11), (segyio.su.sx, 4) ] >>> f.header[11].update(l) :rtype: segyio._header.Header """ return self._header @header.setter def header(self, val): if isinstance(val, Field) or isinstance(val, dict): val = itertools.repeat(val) h, buf = self.header, None for i, v in zip(range(self.tracecount), val): h[i, buf] = v def attributes(self, field): """ File-wide attribute (header word) reading Since v1.1 A range-oriented function that reads some attribute for all the specified headers file-wide. Supports index lookup, slices and numpy-style list-of-indices. Examples: Read all unique sweep frequency end:: >>> end = segyio.TraceField.SweepFrequencyEnd >>> sfe = np.unique(f.attributes( end )[:]) Discover the first traces of each unique sweep frequency end:: >>> end = segyio.TraceField.SweepFrequencyEnd >>> attrs = f.attributes(end) >>> sfe, tracenos = np.unique(attrs[:], return_index = True) Scatter plot group x/y-coordinates with SFEs (using matplotlib):: >>> end = segyio.TraceField.SweepFrequencyEnd >>> attrs = f.attributes(end) >>> _, tracenos = np.unique(attrs[:], return_index = True) >>> gx = f.attributes(segyio.TraceField.GroupX)[tracenos] >>> gy = f.attributes(segyio.TraceField.GroupY)[tracenos] >>> scatter(gx, gy) """ class attr: def __getitem__(inner, rng): try: iter(rng) except TypeError: pass else: return inner._getitem_list(rng) if not isinstance(rng, slice): rng = slice(rng, rng + 1, 1) traces = self.tracecount start, stop, step = rng.indices(traces) attrs = np.empty(len(range(*rng.indices(traces))), dtype = np.intc) return self.xfd.field_forall(attrs, start, stop, step, field) def _getitem_list(inner, xs): if not isinstance(xs, np.ndarray): xs = np.asarray(xs, dtype = np.intc) xs = xs.astype(dtype = np.intc, order = 'C', copy = False) attrs = np.empty(len(xs), dtype = np.intc) return self.xfd.field_foreach(attrs, xs, field) return attr() @property def trace(self): """ Interact with segy in trace mode. Since v1.1 This mode gives access to reading and writing functionality for traces. The primary data type is the numpy ndarray. Traces can be accessed individually or with python slices, and writing is done via assignment. All examples use `np` for `numpy`. Examples: Read all traces in file f and store in a list:: >>> l = [np.copy(tr) for tr in f.trace] Do numpy operations on a trace:: >>> tr = f.trace[10] >>> tr = np.transpose(tr) >>> tr = tr * 2 >>> tr = tr - 100 >>> avg = np.average(tr) Do numpy operations on every other trace:: >>> for tr in f.trace[::2]: ... print( np.average(tr) ) ... Traverse traces in reverse:: >>> for tr in f.trace[::-1]: ... print( np.average(tr) ) ... Double every trace value and write to disk. Since accessing a trace gives a numpy value, to write to the respective trace we need its index:: >>> for i, tr in enumerate(f.trace): ... tr = tr * 2 ... f.trace[i] = tr ... Reuse an array for memory efficiency when working with indices. When using slices or full ranges this is done for you:: >>> tr = None >>> for i in range(100): ... tr = f.trace[i, tr] ... tr = tr * 2 ... print(np.average(tr)) ... Read a value directly from a file. The second [] is numpy access and supports all numpy operations, including negative indexing and slicing:: >>> f.trace[0][0] 1490.2 >>> f.trace[0][1] 1490.8 >>> f.trace[0][-1] 1871.3 >>> f.trace[-1][100] 1562.0 Trace mode supports len(), returning the number of traces in a file:: >>> len(f.trace) 300 Convenient way for setting traces from 0, 1, ... n, based on the iterable set of traces on the right-hand-side. If the right-hand-side traces are exhausted before all the destination file traces the writing will stop, i.e. not all all traces in the destination file will be written. Copy traces from file f to file g:: >>> f.trace = g.trace. Copy first half of the traces from g to f:: >>> f.trace = g.trace[:len(g.trace)/2] Fill the file with one trace (filled with zeros):: >>> tr = np.zeros(f.samples) >>> f.trace = itertools.repeat(tr) For advanced users: sometimes you want to load the entire segy file to memory and apply your own structural manipulations or operations on it. Some segy files are very large and may not fit, in which case this feature will break down. This is an optimisation feature; using it should generally be driven by measurements. Read the first 10 traces:: >>> f.trace.raw[0:10] Read *all* traces to memory:: >>> f.trace.raw[:] Read every other trace to memory:: >>> f.trace.raw[::2] """ return self._trace @trace.setter def trace(self, val): tr = self.trace for i, v in zip(range(len(tr)), val): tr[i] = v def _shape_buffer(self, shape, buf): if buf is None: return np.empty(shape=shape, dtype=np.single) if not isinstance(buf, np.ndarray): return buf if buf.dtype != np.single: return np.empty(shape=shape, dtype=np.single) if buf.shape[0] == shape[0]: return buf if buf.shape != shape and buf.size == np.prod(shape): return buf.reshape(shape) return buf def _line_buffer(self, length, buf=None): shape = (length, len(self.samples)) return self._shape_buffer(shape, buf) def _fread_line(self, trace0, length, stride, buf): offsets = len(self.offsets) return self.xfd.getline(trace0, length, stride, offsets, buf) @property def ilines(self): """ :rtype: numpy.ndarray""" return self._ilines @property def iline(self): """ Interact with segy in inline mode. Since v1.1 This mode gives access to reading and writing functionality for inlines. The primary data type is the numpy ndarray. Inlines can be accessed individually or with python slices, and writing is done via assignment. Note that accessing inlines uses the line numbers, not their position, so if a files has inlines [2400..2500], accessing line [0..100] will be an error. Note that each line is returned as a numpy array, meaning accessing the intersections of the inline and crossline is 0-indexed. Additionally, the iline mode has a concept of offsets, which is useful when dealing with prestack files. Offsets are accessed via so-called sub indexing, meaning iline[10, 4] will give you line 10 at offset 4. Please note that offset, like lines, are accessed via their numbers, not their indices. If your file has the offsets [150, 250, 350, 450] and the lines [2400..2500], you can access the third offset with [2403,350]. Please refer to the examples for more details. If no offset is specified, segyio will give you the first. Examples: Read an inline:: >>> il = f.iline[2400] Copy every inline into a list:: >>> l = [np.copy(x) for x in f.iline] The number of inlines in a file:: >>> len(f.iline) Numpy operations on every other inline:: >>> for line in f.iline[::2]: ... line = line * 2 ... avg = np.average(line) ... print(avg) ... Read inlines up to 2430:: >>> for line in f.iline[:2430]: ... print(np.average(line)) ... Copy a line from file g to f:: >>> f.iline[2400] = g.iline[2834] Copy lines from the first line in g to f, starting at 2400, ending at 2410 in f:: >>> f.iline[2400:2411] = g.iline Convenient way for setting inlines, from left-to-right as the inline numbers are specified in the file.ilines property, from an iterable set on the right-hand-side. If the right-hand-side inlines are exhausted before all the destination file inlines the writing will stop, i.e. not all all inlines in the destination file will be written. Copy inlines from file f to file g:: >>> f.iline = g.iline. Copy first half of the inlines from g to f:: >>> f.iline = g.iline[:g.ilines[len(g.ilines)/2]] Copy every other inline from a different file:: >>> f.iline = g.iline[::2] Accessing offsets work the same way as accessing lines, and slicing is supported as well. When doing range-based offset access, the lines will be generated offsets-first, i.e equivalent to: [(line1 off1), (line1 off2), (line1 off3), (line2 off1), ...] or the double for loop:: >>> for line in lines: ... for off in offsets: ... yield (line, off) ... Copy all lines at all offsets:: >>> [np.copy(x) for x in f.iline[:,:]] Print all line 10's offsets:: >>> print(f.iline[10,:]) Numpy operations at every line at offset 120:: >>> for line in f.iline[:, 120]: ... line = line * 2 ... print(np.average(line)) Copy every other line and offset:: >>> map(np.copy, f.iline[::2, ::2]) Print offsets in reverse:: >>> for line in f.iline[:, ::-1]: ... print(line) Copy all offsets [200, 250, 300, 350, ...] in the range [200, 800) for all ilines [2420,2460):: >>> [np.copy(x) for x in f.iline[2420:2460, 200:800:50]] Copy every third offset from f to g:: >>> g.iline[:,:] = f.iline[:,::3] Copy an iline from f to g at g's offset 200:: >>> g.iline[12, 200] = f.iline[21] """ if self.unstructured: raise ValueError(self._unstructured_errmsg) if self._iline is not None: return self._iline il_len, il_stride = self._iline_length, self._iline_stride lines = self.ilines other_lines = self.xlines buffn = lambda x=None: self._line_buffer(il_len, x) readfn = self._fread_line def writefn(t0, length, step, val): val = buffn(val) step *= len(self.offsets) for i, v in zip(range(t0, t0 + (step * length), step), val): Trace.write_trace(i, v, self) self._iline = Line(self, il_len, il_stride, lines, other_lines, buffn, readfn, writefn, "inline") return self._iline @iline.setter def iline(self, value): self.iline[:] = value @property def xlines(self): """ :rtype: numpy.ndarray""" return self._xlines @property def xline(self): """ Interact with segy in crossline mode. Since v1.1 This mode gives access to reading and writing functionality for crosslines. The primary data type is the numpy ndarray. crosslines can be accessed individually or with python slices, and writing is done via assignment. Note that accessing crosslines uses the line numbers, not their position, so if a files has crosslines [1400..1450], accessing line [0..100] will be an error. Note that each line is returned as a numpy array, meaning accessing the intersections of the inline and crossline is 0-indexed. Additionally, the xline mode has a concept of offsets, which is useful when dealing with prestack files. Offsets are accessed via so-called sub indexing, meaning xline[10, 4] will give you line 10 at offset 4. Please note that offset, like lines, are accessed via their numbers, not their indices. If your file has the offsets [100, 200, 300, 400] and the lines [1400..1450], you can access the second offset with [1421,300]. Please refer to the examples for more details. If no offset is specified, segyio will give you the first. Examples: Read an crossline:: >>> il = f.xline[1400] Copy every crossline into a list:: >>> l = [np.copy(x) for x in f.xline] The number of crosslines in a file:: >>> len(f.xline) Numpy operations on every third crossline:: >>> for line in f.xline[::3]: ... line = line * 6 ... avg = np.average(line) ... print(avg) ... Read crosslines up to 1430:: >>> for line in f.xline[:1430]: ... print(np.average(line)) ... Copy a line from file g to f:: >>> f.xline[1400] = g.xline[1603] Copy lines from the first line in g to f, starting at 1400, ending at 1415 in f:: >>> f.xline[1400:1416] = g.xline Convenient way for setting crosslines, from left-to-right as the crossline numbers are specified in the file.xlines property, from an iterable set on the right-hand-side. If the right-hand-side crosslines are exhausted before all the destination file crosslines the writing will stop, i.e. not all all crosslines in the destination file will be written. Copy crosslines from file f to file g:: >>> f.xline = g.xline. Copy first half of the crosslines from g to f:: >>> f.xline = g.xline[:g.xlines[len(g.xlines)/2]] Copy every other crossline from a different file:: >>> f.xline = g.xline[::2] Accessing offsets work the same way as accessing lines, and slicing is supported as well. When doing range-based offset access, the lines will be generated offsets-first, i.e equivalent to: [(line1 off1), (line1 off2), (line1 off3), (line2 off1), ...] or the double for loop:: >>> for line in lines: ... for off in offsets: ... yield (line, off) ... Copy all lines at all offsets:: >>> [np.copy(x) for x in f.xline[:,:]] Print all line 10's offsets:: >>> print(f.xline[10,:]) Numpy operations at every line at offset 120:: >>> for line in f.xline[:, 120]: ... line = line * 2 ... print(np.average(line)) Copy every other line and offset:: >>> map(np.copy, f.xline[::2, ::2]) Print offsets in reverse:: >>> for line in f.xline[:, ::-1]: ... print(line) Copy all offsets [200, 250, 300, 350, ...] in the range [200, 800) for all xlines [2420,2460):: >>> [np.copy(x) for x in f.xline[2420:2460, 200:800:50]] Copy every third offset from f to g:: >>> g.xline[:,:] = f.xline[:,::3] Copy an xline from f to g at g's offset 200:: >>> g.xline[12, 200] = f.xline[21] """ if self.unstructured: raise ValueError(self._unstructured_errmsg) if self._xline is not None: return self._xline xl_len, xl_stride = self._xline_length, self._xline_stride lines = self.xlines other_lines = self.ilines buffn = lambda x=None: self._line_buffer(xl_len, x) readfn = self._fread_line def writefn(t0, length, step, val): val = buffn(val) step *= len(self.offsets) for i, v in zip(range(t0, t0 + step * length, step), val): Trace.write_trace(i, v, self) self._xline = Line(self, xl_len, xl_stride, lines, other_lines, buffn, readfn, writefn, "crossline") return self._xline @xline.setter def xline(self, value): self.xline[:] = value def _depth_buffer(self, buf=None): il_len = self._iline_length xl_len = self._xline_length if self.sorting == TraceSortingFormat.CROSSLINE_SORTING: shape = (il_len, xl_len) elif self.sorting == TraceSortingFormat.INLINE_SORTING: shape = (xl_len, il_len) else: raise RuntimeError("Unexpected sorting type") return self._shape_buffer(shape, buf) @property def fast(self): """ Access the 'fast' dimension Since v1.1 This mode yields iline or xline mode, depending on which one is laid out "faster", i.e. the line with linear disk layout. Use this mode if the inline/crossline distinction isn't as interesting as traversing in a fast manner (typically when you want to apply a function to the whole file, line-by-line). """ if self.sorting == TraceSortingFormat.INLINE_SORTING: return self.iline elif self.sorting == TraceSortingFormat.CROSSLINE_SORTING: return self.xline else: raise RuntimeError("Unknown sorting.") @property def slow(self): """ Access the 'slow' dimension Since v1.1 This mode yields iline or xline mode, depending on which one is laid out "slower", i.e. the line with strided disk layout. Use this mode if the inline/crossline distinction isn't as interesting as traversing in the slower direction. """ if self.sorting == TraceSortingFormat.INLINE_SORTING: return self.xline elif self.sorting == TraceSortingFormat.CROSSLINE_SORTING: return self.iline else: raise RuntimeError("Unknown sorting.") @property def depth_slice(self): """ Interact with segy in depth slice mode. Since v1.1 This mode gives access to reading and writing functionality for depth slices, a horizontal cut of the survey. The primary data type is the numpy ndarray. Depth slices can be accessed individually or with python slices, and writing is done via assignment. Note that each slice is returned as a numpy array, meaning accessing the values of the slice is 0-indexed. Examples: Read a depth slice: >>> il = f.depth_slice[199] Copy every depth slice into a list:: >>> l = [np.copy(x) for x in f.depth_slice] The number of depth slices in a file:: >>> len(f.depth_slice) Numpy operations on every third depth slice:: >>> for depth_slice in f.depth_slice[::3]: ... depth_slice = depth_slice * 6 ... avg = np.average(depth_slice) ... print(avg) ... Read depth_slices up to 250:: >>> for depth_slice in f.depth_slice[:250]: ... print(np.average(depth_slice)) ... Copy a slice from file g to f:: >>> f.depth_slice[4] = g.depth_slice[19] Copy slice from the first line in g to f, starting at 10, ending at 49 in f:: >>> f.depth_slice[10:50] = g.depth_slice Convenient way for setting depth slices, from left-to-right as the depth slices numbers are specified in the file.depth_slice property, from an iterable set on the right-hand-side. If the right-hand-side depth slices are exhausted before all the destination file depth slices the writing will stop, i.e. not all all depth slices in the destination file will be written. Copy depth slices from file f to file g:: >>> f.depth_slice = g.depth_slice Copy first half of the depth slices from g to f:: >>> f.depth_slice = g.depth_slice[:g.samples/2]] Copy every other depth slices from a different file:: >>> f.depth_slice = g.depth_slice[::2] """ if self.unstructured: raise ValueError(self._unstructured_errmsg) indices = np.asarray(list(range(len(self.samples))), dtype=np.intc) other_indices = np.asarray([0], dtype=np.intc) buffn = self._depth_buffer slice_trace_count = self._iline_length * self._xline_length offsets = len(self.offsets) def readfn(depth, length, stride, buf): return self.xfd.getdepth(depth, slice_trace_count, offsets, buf) def writefn(depth, length, stride, val): val = buffn(val) buf_view = val.reshape(self._iline_length * self._xline_length) for i, trace_buf in enumerate(self.trace): trace_buf[depth] = buf_view[i] self.trace[i] = trace_buf return Line(self, len(self.samples), 1, indices, other_indices, buffn, readfn, writefn, "depth") @depth_slice.setter def depth_slice(self, value): self.depth_slice[:] = value @property def gather(self): """ Interact with segy in gather mode Since v1.1 A gather is in this context the intersection of lines in a cube, i.e. all the offsets at some iline/xline intersection. The primary data type is the numpy ndarray, with dimensions depending on the range of offsets specified. Offsets uses the line and offset numbers (names), not 0-based indices. When using ranges over lines, a generator is returned. Examples: Read one offset at an intersection:: >>> f.gather[200, 241, 25] # returns a samples-long 1d-array Read all offsets at an intersection:: >>> f.gather[200, 241, :] # returns offsets x samples ndarray >>> # If no offset is specified, this is implicitly (:) >>> f.gather[200, 241, :] == f.gather[200, 241] All offsets for a set of ilines, intersecting one crossline:: >>> f.gather[200:300, 241, :] Some offsets for a set of ilines, interescting one crossline:: >>> f.gather[200:300, 241, 10:25:5] Some offsets for a set of ilines and xlines. This effectively yields a subcube:: >>> f.gather[200:300, 241:248, 1:10] """ if self.unstructured: raise ValueError(self._unstructured_errmsg) if self._gather is not None: return self._gather self._gather = Gather(self.trace, self.iline, self.xline, self.offsets, self.sorting) return self._gather @property def text(self): """ Interact with segy in text mode. Since v1.1 This mode gives access to reading and writing functionality for textual headers. The primary data type is the python string. Reading textual headers is done via [], and writing is done via assignment. No additional structure is built around the textual header, so everything is treated as one long string without line breaks. Examples: Print the textual header:: >>> print(f.text[0]) Print the first extended textual header:: >>> print(f.text[1]) Write a new textual header:: >>> f.text[0] = make_new_header() Copy a tectual header:: >>> f.text[1] = g.text[0] Print a textual header line-by-line:: >>> # using zip, from the zip documentation >>> text = f.text[0] >>> lines = map(''.join, zip( *[iter(text)] * 80)) >>> for line in lines: ... print(line) ... """ return TextHeader(self) @property def bin(self): """ Interact with segy in binary mode. Since v1.1 This mode gives access to reading and writing functionality for the binary header. Please note that using numeric binary offsets uses the offset numbers from the specification, i.e. the first field of the binary header starts at 3201, not 1. If you're using the enumerations this is handled for you. Examples: Copy a header from file g to file f:: >>> f.bin = g.bin Reading a field in a trace:: >>> traces_per_ensemble = f.bin[3213] Writing a field in a trace:: >>> f.bin[BinField.Traces] = 5 Reading multiple fields:: >>> d = f.bin[BinField.Traces, 3233] Copy a field from file g to file f:: >>> f.bin[BinField.Format] = g.bin[BinField.Format] Copy full binary from file f to file g:: >>> f.bin = g.bin Copy multiple fields from file f to file g:: >>> f.bin = g.bin[BinField.Traces, 3233] Write field in binary header via dict:: >>> f.bin = { BinField.Traces: 350 } Write multiple fields in a trace:: >>> f.bin = { 3213: 5, BinField.SweepFrequencyStart: 17 } Since v1.3, common dict operations are supported. Get a list of keys and values:: >>> f.bin.keys() >>> f.bin.values() Get a list of key-value pairs:: >>> f.bin.items() Get the number of keys-value pairs in a header:: >>> len(f.bin) Update a set of values:: >>> d = { segyio.su.jobid: 10, segyio.su.lino: 5 } >>> f.bin.update(d) >>> l = [ (segyio.su.hdt, 11), (segyio.su.hsfs, 4) ] >>> f.bin.update(l) """ return Field.binary(self) @bin.setter def bin(self, value): self.bin.update(value) @property def format(self): d = { 1: "4-byte IBM float", 2: "4-byte signed integer", 3: "2-byte signed integer", 4: "4-byte fixed point with gain", 5: "4-byte IEEE float", 8: "1-byte signed char" } class fmt: def __int__(inner): return self._fmt def __str__(inner): if not self._fmt in d: return "Unknown format" return d[self._fmt] return fmt() class spec: def __init__(self): self.iline = 189 self.ilines = None self.xline = 193 self.xlines = None self.offsets = [1] self.samples = None self.ext_headers = 0 self.format = None self.sorting = None class TextHeader(object): def __init__(self, outer): self.outer = outer def __getitem__(self, index): if not 0 <= index <= self.outer.ext_headers: raise IndexError("Textual header {} not in file".format(index)) return self.outer.xfd.gettext(index) def __setitem__(self, index, val): if isinstance(val, TextHeader): self[index] = val[0] return if not 0 <= index <= self.outer.ext_headers: raise IndexError("Textual header {} not in file".format(index)) self.outer.xfd.puttext(index, val) def __repr__(self): return "Text(external_headers = {})".format(self.outer.ext_headers) def __str__(self): return '\n'.join(map(''.join, zip(*[iter(str(self[0]))] * 80))) segyio-1.5.2/python/segyio/_header.py0000664000372000037200000001012113253720451017176 0ustar travistravisimport itertools try: from future_builtins import zip except ImportError: pass import segyio from segyio._line import Line from segyio._field import Field class Header(object): def __init__(self, segy): self.segy = segy @staticmethod def _header_buffer(buf=None): if buf is None: buf = bytearray(segyio._segyio.thsize()) return buf def __getitem__(self, traceno, buf=None): if isinstance(traceno, tuple): return self.__getitem__(traceno[0], traceno[1]) buf = self._header_buffer(buf) if isinstance(traceno, slice): def gen(): buf1, buf2 = self._header_buffer(), self._header_buffer() for i in range(*traceno.indices(self.segy.tracecount)): x = self.__getitem__(i, buf1) buf2, buf1 = buf1, buf2 yield x return gen() return Field.trace(buf, traceno=traceno, segy=self.segy) def __setitem__(self, traceno, val): buf = None # library-provided loops can re-use a buffer for the lookup, even in # __setitem__, so we might need to unpack the tuple to reuse the buffer if isinstance(traceno, tuple): buf = traceno[1] traceno = traceno[0] self.__getitem__(traceno, buf).update(val) def __iter__(self): return self[:] def __repr__(self): return "Header(traces = {})".format(self.segy.samples) def readfn(self, t0, length, stride, *_): def gen(): buf1, buf2 = self._header_buffer(), self._header_buffer() start = t0 step = stride * len(self.segy.offsets) stop = t0 + (length * step) for i in range(start, stop, step): x = Field.trace(buf1, traceno=i, segy=self.segy) buf2, buf1 = buf1, buf2 yield x return gen() def writefn(self, t0, length, stride, val): start = t0 stride *= len(self.segy.offsets) stop = t0 + (length * stride) if isinstance(val, Field) or isinstance(val, dict): val = itertools.repeat(val) for i, x in zip(range(start, stop, stride), val): self[i] = x @property def iline(self): """:rtype: Line""" segy = self.segy length = segy._iline_length stride = segy._iline_stride lines = segy.ilines other_lines = segy.xlines buffn = self._header_buffer return Line(segy, length, stride, lines, other_lines, buffn, self.readfn, self.writefn, "Inline") @iline.setter def iline(self, value): """Write iterables to lines Examples: Supports writing to *all* crosslines via assignment, regardless of data source and format. Will respect the sample size and structure of the file being assigned to, so if the argument traces are longer than that of the file being written to the surplus data will be ignored. Uses same rules for writing as `f.iline[i] = x`. """ for i, src in zip(self.segy.ilines, value): self.iline[i] = src @property def xline(self): """:rtype: Line""" segy = self.segy length = segy._xline_length stride = segy._xline_stride lines = segy.xlines other_lines = segy.ilines buffn = self._header_buffer return Line(segy, length, stride, lines, other_lines, buffn, self.readfn, self.writefn, "Crossline") @xline.setter def xline(self, value): """Write iterables to lines Examples: Supports writing to *all* crosslines via assignment, regardless of data source and format. Will respect the sample size and structure of the file being assigned to, so if the argument traces are longer than that of the file being written to the surplus data will be ignored. Uses same rules for writing as `f.xline[i] = x`. """ for i, src in zip(self.segy.xlines, value): self.xline[i] = src segyio-1.5.2/python/segyio/tools.py0000664000372000037200000002021113253720451016750 0ustar travistravisimport segyio import numpy as np import textwrap def dt(f, fallback_dt=4000.0): """Since v1.1 Find a *dt* value in the SegyFile. If none is found use the provided *fallback_dt* value. :type f: segyio.SegyFile :type fallback_dt: float :rtype: float """ return f.xfd.getdt(fallback_dt) def sample_indexes(segyfile, t0=0.0, dt_override=None): """Since v1.1 Creates a list of values representing the samples in a trace at depth or time. The list starts at *t0* and is incremented with am*dt* for the number of samples. If a *dt_override* is not provided it will try to find a *dt* in the file. :type segyfile: segyio.SegyFile :type t0: float :type dt_override: float or None :rtype: list[float] """ if dt_override is None: dt_override = dt(segyfile) return [t0 + t * dt_override for t in range(len(segyfile.samples))] def create_text_header(lines): """ Will create a "correct" SEG-Y textual header. Every line will be prefixed with C## and there are 40 lines. The input must be a dictionary with the line number[1-40] as a key. The value for each key should be up to 76 character long string. :type lines: dict[int, str] :rtype: str """ rows = [] for line_no in range(1, 41): line = "" if line_no in lines: line = lines[line_no] row = "C{0:>2} {1:76}".format(line_no, line) rows.append(row) rows = ''.join(rows) return rows def wrap(s, width=80): """Since v1.1 Formats the text input with newlines given the user specified width for each line :type s: str :type width: int :rtype: str """ return '\n'.join(textwrap.wrap(str(s), width=width)) def native(data, format = segyio.SegySampleFormat.IBM_FLOAT_4_BYTE, copy = True): """ Convert numpy array to native float Since v1.1 :type data: numpy.ndarray :type format: int|segyio.SegySampleFormat :type copy: bool :rtype: numpy.ndarray Converts a numpy array from raw segy trace data to native floats. Works for numpy ndarrays. Examples: Convert mmap'd trace to native float: >>> d = np.memmap('file.sgy', offset = 3600, dtype = np.uintc) >>> samples = 1500 >>> trace = segyio.tools.native(d[240:240+samples]) """ data = data.view( dtype = np.single ) if copy: data = np.copy( data ) format = int(segyio.SegySampleFormat(format)) return segyio._segyio.native(data, format) def collect(itr): """ Collect traces or lines into one ndarray Since v1.1 Eagerly copy a series of traces, lines or depths into one numpy ndarray. If collecting traces or fast-direction over a post-stacked file, reshaping the resulting array is equivalent to calling `tools.cube`. Examples: collect-cube identity:: >>> f = segyio.open('post-stack.sgy') >>> x = segyio.tools.collect(f.traces[:]) >>> x = x.reshape((len(f.ilines), len(f.xlines), f.samples)) >>> numpy.all(x == segyio.tools.cube(f)) :type itr: iterable[numpy.ndarray] :rtype: numpy.ndarray """ return np.stack([np.copy(x) for x in itr]) def cube(f): """ Read a full cube from a file Since v1.1 Takes an open segy file (created with segyio.open) or a file name. If the file is a prestack file, the cube returned has the dimensions (fast,slow,offset,sample). If it is post-stack (i.e. only the one offset), the dimensions are normalised to (fast,slow,sample) :type f: SegyFile|str :rtype numpy.ndarray """ if not isinstance(f, segyio.SegyFile): with segyio.open(f) as fl: return cube(fl) ilsort = f.sorting == segyio.TraceSortingFormat.INLINE_SORTING fast = f.ilines if ilsort else f.xlines slow = f.xlines if ilsort else f.ilines fast, slow, offs = len(fast), len(slow), len(f.offsets) smps = len(f.samples) dims = (fast, slow, smps) if offs == 1 else (fast, slow, offs, smps) return f.trace.raw[:].reshape(dims) def rotation(f, line = 'fast'): """ Find rotation of the survey Since v1.2 Find the clock-wise rotation and origin of `line` as (rot,cdp-x,cdp-y) The clock-wise rotation is defined as the angle in radians between line given by the first and last trace of the first line and the axis that gives increasing CDP-Y, in the direction that gives increasing CDP-X. By default, the first line is the 'fast' direction, which is inlines if the file is inline sorted, and crossline if it's crossline sorted. `line` should be any of 'fast', 'slow', 'iline', and 'xline'. :type f: SegyFile :type line: str :rtype (float, int, int) """ if f.unstructured: raise ValueError("Rotation requires a structured file") lines = { 'fast': f.fast, 'slow': f.slow, 'iline': f.iline, 'xline': f.xline, } if line not in lines: error = "Unknown line {}".format(line) solution = "Must be any of: {}".format(' '.join(lines.keys())) raise ValueError('{} {}'.format(error, solution)) l = lines[line] origin = f.header[0][segyio.su.cdpx, segyio.su.cdpy] cdpx, cdpy = origin[segyio.su.cdpx], origin[segyio.su.cdpy] rot = f.xfd.rotation( l.len, l.stride, len(f.offsets), np.asarray(l.lines, order = 'C', dtype = np.intc) ) return rot, cdpx, cdpy def metadata(f): """ Get survey structural properties and metadata Since v1.4 Create a descriptor object that, when passed to `segyio.create()`, would create a new file with the same structure, dimensions and metadata as `f`. Takes an open segy file (created with segyio.open) or a file name. :type f: SegyFile|str :rtype segyio.spec """ if not isinstance(f, segyio.SegyFile): with segyio.open(f) as fl: return metadata(fl) spec = segyio.spec() spec.iline = f._il spec.xline = f._xl spec.samples = f.samples spec.format = f.format spec.ilines = f.ilines spec.xlines = f.xlines spec.offsets = f.offsets spec.sorting = f.sorting spec.tracecount = f.tracecount spec.ext_headers = f.ext_headers return spec def resample(f, rate = None, delay = None, micro = False, trace = True, binary = True): """ Resample a file Since v1.4 Resample all data traces, and update the file handle to reflect the new sample rate. No actual samples (data traces) are modified, only the header fields and interpretation. By default, the rate and the delay are in millseconds - if you need higher resolution, passing micro=True interprets rate as microseconds (as it is represented in the file). Delay is always milliseconds. By default, both the global binary header and the trace headers are updated to reflect this. If preserving either the trace header interval field or the binary header interval field is important, pass trace=False and binary=False respectively, to not have that field updated. This only apply to sample rates - the recording delay is only found in trace headers and will be written unconditionally, if delay is not None. This function requires an open file handle and is DESTRUCTIVE. It will modify the file, and if an exception is raised then partial writes might have happened and the file might be corrupted. This function assumes all traces have uniform delays and frequencies. :type f: SegyFile :type rate: int :type delay: int :type micro: bool :type trace: bool :type binary: bool """ if rate is not None: if not micro: rate *= 1000 if binary: f.bin[segyio.su.hdt] = rate if trace: f.header = { segyio.su.dt: rate} if delay is not None: f.header = { segyio.su.delrt: delay } t0 = delay if delay is not None else f.samples[0] rate = rate / 1000 if rate is not None else f.samples[1] - f.samples[0] f._samples = (np.arange(len(f.samples), dtype = np.single) * rate) + t0 return f segyio-1.5.2/python/segyio/su.py0000664000372000037200000001114613253720451016246 0ustar travistravisfrom segyio import Enum from segyio import TraceField as tf from segyio import BinField as bf class su(Enum): """ Since v1.1 Seismic Unix style aliases for binary and trace header fields. About names: Seismic Unix does not have names for all possible fields, as it came around during an early revision of SEG-Y, and names for these fields are created by segyio in their absence. If Seismic Unix starts providing names for these fields, they will be added to these alies, and in conflicts take presedence after some time. If there are no conflicts, segyio names are considered for deprecation on a case-by-case basis, but will most likely be supported along with the Seismic Unix name. """ # trace tracl = tf.TRACE_SEQUENCE_LINE tracr = tf.TRACE_SEQUENCE_FILE fldr = tf.FieldRecord tracf = tf.TraceNumber ep = tf.EnergySourcePoint cdp = tf.CDP cdpt = tf.CDP_TRACE trid = tf.TraceIdentificationCode nvs = tf.NSummedTraces nhs = tf.NStackedTraces duse = tf.DataUse offset = tf.offset gelev = tf.ReceiverGroupElevation selev = tf.SourceSurfaceElevation sdepth = tf.SourceDepth gdel = tf.ReceiverDatumElevation sdel = tf.SourceDatumElevation swdep = tf.SourceWaterDepth gwdep = tf.GroupWaterDepth scalel = tf.ElevationScalar scalco = tf.SourceGroupScalar sx = tf.SourceX sy = tf.SourceY gx = tf.GroupX gy = tf.GroupY counit = tf.CoordinateUnits wevel = tf.WeatheringVelocity swevel = tf.SubWeatheringVelocity sut = tf.SourceUpholeTime gut = tf.GroupUpholeTime sstat = tf.SourceStaticCorrection gstat = tf.GroupStaticCorrection tstat = tf.TotalStaticApplied laga = tf.LagTimeA lagb = tf.LagTimeB delrt = tf.DelayRecordingTime muts = tf.MuteTimeStart mute = tf.MuteTimeEND ns = tf.TRACE_SAMPLE_COUNT dt = tf.TRACE_SAMPLE_INTERVAL gain = tf.GainType igc = tf.InstrumentGainConstant igi = tf.InstrumentInitialGain corr = tf.Correlated sfs = tf.SweepFrequencyStart sfe = tf.SweepFrequencyEnd slen = tf.SweepLength styp = tf.SweepType stat = tf.SweepTraceTaperLengthStart stae = tf.SweepTraceTaperLengthEnd tatyp = tf.TaperType afilf = tf.AliasFilterFrequency afils = tf.AliasFilterSlope nofilf = tf.NotchFilterFrequency nofils = tf.NotchFilterSlope lcf = tf.LowCutFrequency hcf = tf.HighCutFrequency lcs = tf.LowCutSlope hcs = tf.HighCutSlope year = tf.YearDataRecorded day = tf.DayOfYear hour = tf.HourOfDay minute = tf.MinuteOfHour sec = tf.SecondOfMinute timbas = tf.TimeBaseCode trwf = tf.TraceWeightingFactor grnors = tf.GeophoneGroupNumberRoll1 grnofr = tf.GeophoneGroupNumberFirstTraceOrigField grnlof = tf.GeophoneGroupNumberLastTraceOrigField gaps = tf.GapSize otrav = tf.OverTravel cdpx = tf.CDP_X cdpy = tf.CDP_Y iline = tf.INLINE_3D xline = tf.CROSSLINE_3D sp = tf.ShotPoint scalsp = tf.ShotPointScalar trunit = tf.TraceValueMeasurementUnit tdcm = tf.TransductionConstantMantissa tdcp = tf.TransductionConstantPower tdunit = tf.TransductionUnit triden = tf.TraceIdentifier sctrh = tf.ScalarTraceHeader stype = tf.SourceType sedm = tf.SourceEnergyDirectionMantissa sede = tf.SourceEnergyDirectionExponent smm = tf.SourceMeasurementMantissa sme = tf.SourceMeasurementExponent smunit = tf.SourceMeasurementUnit uint1 = tf.UnassignedInt1 uint2 = tf.UnassignedInt2 # binary jobid = bf.JobID lino = bf.LineNumber reno = bf.ReelNumber ntrpr = bf.Traces nart = bf.AuxTraces hdt = bf.Interval dto = bf.IntervalOriginal hns = bf.Samples nso = bf.SamplesOriginal format = bf.Format fold = bf.EnsembleFold tsort = bf.SortingCode vscode = bf.VerticalSum hsfs = bf.SweepFrequencyStart hsfe = bf.SweepFrequencyEnd hslen = bf.SweepLength hstyp = bf.Sweep schn = bf.SweepChannel hstas = bf.SweepTaperStart hstae = bf.SweepTaperEnd htatyp = bf.Taper hcorr = bf.CorrelatedTraces bgrcv = bf.BinaryGainRecovery rcvm = bf.AmplitudeRecovery mfeet = bf.MeasurementSystem polyt = bf.ImpulseSignalPolarity vpol = bf.VibratoryPolarity unas1 = bf.Unassigned1 rev = bf.SEGYRevision trflag = bf.TraceFlag exth = bf.ExtendedHeaders unas2 = bf.Unassigned2 segyio-1.5.2/python/segyio/open.py0000664000372000037200000001205013253720451016553 0ustar travistravisimport numpy import segyio def open(filename, mode="r", iline = 189, xline = 193, strict = True, ignore_geometry = False): """Open a segy file. Since v1.1 Opens a segy file and tries to figure out its sorting, inline numbers, crossline numbers, and offsets, and enables reading and writing to this file in a simple manner. For reading, the access mode "r" is preferred. All write operations will raise an exception. For writing, the mode "r+" is preferred (as "rw" would truncate the file). Any mode with 'w' will raise an error. The modes used are standard C file modes; please refer to that documentation for a complete reference. Open should be used together with python's `with` statement. Please refer to the examples. When the `with` statement is used the file will automatically be closed when the routine completes or an exception is raised. By default, segyio tries to open in 'strict' mode. This means the file will be assumed to represent a geometry with consistent inline, crosslines and offsets. If strict is False, segyio will still try to establish a geometry, but it won't abort if it fails. When in non-strict mode is opened, geometry-dependent modes such as iline will raise an error. If 'ignore_geometry' is set to True, segyio will *not* try to build iline/xline or other geometry related structures, which leads to faster opens. This is essentially the same as using strict = False on a file that has no geometry. Args: filename (str-like): Path to file to open. mode (str, optional): File access mode, defaults to "r". iline (TraceField): Inline number field in the trace headers. Defaults to 189 as per the SEGY specification. xline (TraceField): Crossline number field in the trace headers. Defaults to 193 as per the SEGY specification. strict (bool, optional): Abort if a geometry cannot be inferred. Defaults to True. ignore_geometry (bool, optional): Opt out on building geometry information, useful for e.g. shot organised files. Defaults to False. Examples: Open a file in read-only mode:: >>> with segyio.open(path, "r") as f: ... print(f.ilines) ... Open a file in read-write mode:: >>> with segyio.open(path, "r+") as f: ... f.trace = np.arange(100) ... Open two files at once:: >>> with segyio.open(path) as src, segyio.open(path, "r+") as dst: ... dst.trace = src.trace # copy all traces from src to dst ... :rtype: segyio.SegyFile """ if 'w' in mode: problem = 'w in mode would truncate the file' solution = 'use r+ to open in read-write' raise ValueError(', '.join((problem, solution))) f = segyio.SegyFile(str(filename), mode, iline, xline) metrics = f.xfd.metrics() try: dt = segyio.tools.dt(f, fallback_dt = 4000.0) / 1000.0 t0 = f.header[0][segyio.TraceField.DelayRecordingTime] samples = metrics['samplecount'] f._samples = (numpy.arange(samples, dtype = numpy.single) * dt) + t0 except: f.close() raise if ignore_geometry: return f try: cube_metrics = f.xfd.cube_metrics(iline, xline) f._sorting = cube_metrics['sorting'] iline_count = cube_metrics['iline_count'] xline_count = cube_metrics['xline_count'] offset_count = cube_metrics['offset_count'] metrics.update(cube_metrics) line_metrics = segyio._segyio.line_metrics(f.sorting, f.tracecount, iline_count, xline_count, offset_count) f._iline_length = line_metrics['iline_length'] f._iline_stride = line_metrics['iline_stride'] f._xline_length = line_metrics['xline_length'] f._xline_stride = line_metrics['xline_stride'] f._ilines = numpy.zeros(iline_count, dtype = numpy.intc) f._xlines = numpy.zeros(xline_count, dtype = numpy.intc) f._offsets = numpy.zeros(offset_count, dtype = numpy.intc) f.xfd.indices(metrics, f.ilines, f.xlines, f.offsets) if numpy.unique(f.ilines).size != f.ilines.size: raise ValueError( "Inlines inconsistent - expect all inlines to be unique") if numpy.unique(f.xlines).size != f.xlines.size: raise ValueError( "Crosslines inconsistent - expect all crosslines to be unique") except: if not strict: f._ilines = None f._xlines = None f._offsets = None else: f.close() raise return f segyio-1.5.2/python/segyio/_line.py0000664000372000037200000001175613253720451016714 0ustar travistravisimport itertools try: from future_builtins import zip except ImportError: pass import segyio._segyio as _segyio # in order to support [:end] syntax, we must make sure # start has a non-None value. lineno.indices() would set it # to 0, but we don't know if that's a reasonable value or # not. If start is None we set it to the first line def sanitize_slice(s, source): if all((s.start, s.stop, s.step)): return s start, stop, step = s.start, s.stop, s.step increasing = step is None or step > 0 if start is None: start = source[0] if increasing else source[-1] if stop is None: stop = source[-1] + 1 if increasing else source[0] - 1 return slice(start, stop, step) class Line: """ Line mode for traces and trace headers. Internal. The _line class provides an interface for line-oriented operations. The line reading operations themselves are not streaming - it's assumed that when a line is queried it's somewhat limited in size and will comfortably fit in memory, and that the full line is interesting. This also applies to line headers; however, all returned values support the iterable protocol so they work fine together with the streaming bits of this library. _line should not be instantiated directly by users, but rather returned from the iline/xline properties of file or from the header mode. Any direct construction of this should be considered an error. """ def __init__(self, segy, length, stride, lines, other_lines, buffn, readfn, writefn, name): self.segy = segy self.len = length self.stride = stride self.lines = lines self.other_lines = other_lines self.name = name self.buffn = buffn self.readfn = readfn self.writefn = writefn def __repr__(self): return "Line(direction = {}, length = {})".format(self.name, self.len) def _index(self, lineno, offset): """ :rtype: int""" offs = self.segy.offsets if offset is None: offset = 0 else: try: offset = next(i for i, x in enumerate(offs) if x == offset) except StopIteration: try: int(offset) except TypeError: raise TypeError("Offset must be int or slice") raise KeyError("Unknkown offset {}".format(offset)) try: lineno = int(lineno) except TypeError: raise TypeError("Must be int or slice") trace0 = _segyio.fread_trace0(lineno, len(self.other_lines), self.stride, len(offs), self.lines, self.name) return offset + trace0 def _indices(self, lineno, offset): """ :rtype: tuple[collections.Iterable, collections.Iterable]""" offsets = self.segy.offsets if offset is None: offset = offsets[0] if not isinstance(lineno, slice): lineno = slice(lineno, lineno + 1, 1) if not isinstance(offset, slice): offset = slice(offset, offset + 1, 1) lineno = sanitize_slice(lineno, self.lines) offset = sanitize_slice(offset, offsets) offs, lns = set(self.segy.offsets), set(self.lines) orng = range(*offset.indices(offsets[-1] + 1)) lrng = range(*lineno.indices(self.lines[-1] + 1)) return filter(lns.__contains__, lrng), filter(offs.__contains__, orng) def _get(self, lineno, offset, buf): """ :rtype: numpy.ndarray""" t0 = self._index(lineno, offset) return self.readfn(t0, self.len, self.stride, buf) def _get_iter(self, lineno, off, buf): """ :rtype: collections.Iterable[numpy.ndarray]""" buf1, buf2 = buf, self.buffn() for line, offset in itertools.product(*self._indices(lineno, off)): buf1 = self._get(line, offset, buf1) buf2, buf1 = buf1, buf2 yield buf2 def __getitem__(self, lineno, offset=None): """ :rtype: numpy.ndarray|collections.Iterable[numpy.ndarray]""" buf = self.buffn() if isinstance(lineno, tuple): lineno, offset = lineno if isinstance(lineno, slice) or isinstance(offset, slice): return self._get_iter(lineno, offset, buf) return self._get(lineno, offset, buf) def __setitem__(self, lineno, val): offset = None if isinstance(lineno, tuple): lineno, offset = lineno if isinstance(lineno, slice) or isinstance(offset, slice): indices = itertools.product(*self._indices(lineno, offset)) for (line, offset), x in zip(indices, val): t0 = self._index(line, offset) self.writefn(t0, self.len, self.stride, x) else: t0 = self._index(lineno, offset) self.writefn(t0, self.len, self.stride, val) def __len__(self): return len(self.lines) def __iter__(self): buf = self.buffn() for i in self.lines: yield self._get(i, None, buf) segyio-1.5.2/python/segyio/__init__.py0000664000372000037200000000655113253720451017362 0ustar travistravis""" simple segy input/output Welcome to segyio. For help, examples and reference, type `help(function)` in your favourite python interpreter, or `pydoc function` in the unix console. The segy library attempts to be easy to use efficently for prototyping and interaction with possibly large segy files. File reading and writing is streaming, with large file support out of the box and without hassle. For a quick start on reading files, type `help(segyio.open)`. An open segy file is interacted with in modes, found in the segy module. For a reference with examples, please type `help(segyio.segy)`. For documentation on individual modes, please refer to the individual modes with `help(segyio.SegyFile.[mode])`, or look it up in the aggregated segyio.segy. The available modes are: * text, for textual headers including extended headers * bin, for the binary header * header, for the trace headers * trace, for trace data * iline, for inline biased operations * xline, for crossline biased operations The primary data type is the numpy array. All examples use `np` for the numpy namespace. That means that any function that returns a trace, a set of samples or even full lines, returns a numpy array. This enables quick and easy mathematical operations on the data you care about. Segyio is designed to blend into regular python code, so python concepts that map to segy operations are written to behave similarly. That means that sequences of data support list lookup, slicing (`f.trace[0:10:2]`), `for x in` etc. Please refer to the individual mode's documentation for a more exhaustive list with examples. For all slicing operations that segyio provides the underlying buffer is reused, so if you want to keep the data between iterations it is necessary to manually copy the data. Please refer to the examples. (e.g. numpy.copy()) """ class Enum(object): def __init__(self, enum_value): super(Enum, self).__init__() self._value = int(enum_value) def __int__(self): return int(self._value) def __str__(self): for k, v in self.__class__.__dict__.items(): if isinstance(v, int) and self._value == v: return k return "Unknown Enum" def __repr__(self): return str(self) def __hash__(self): return hash(self._value) def __eq__(self, other): try: o = int(other) except ValueError: return super(Enum, self).__eq__(other) else: return self._value == o def __ne__(self, other): return not self == other @classmethod def enums(cls): result = [] for v in cls.__dict__.values(): if isinstance(v, int): result.append(cls(v)) return sorted(result, key=int) from .binfield import BinField from .segysampleformat import SegySampleFormat from .tracesortingformat import TraceSortingFormat from .tracefield import TraceField from .su import su from .open import open from .create import create from .segy import SegyFile, spec from .tools import dt, sample_indexes, create_text_header, native from .tools import collect, cube try: from .version import version as __version__ except ImportError: __version__ = '0.0.0' __copyright__ = 'Copyright 2016, Statoil ASA' __license__ = 'GNU Lesser General Public License version 3' __status__ = 'Production' segyio-1.5.2/python/segyio/_gather.py0000664000372000037200000000661013253720451017230 0ustar travistravisimport itertools import numpy as np import segyio.tools as tools from segyio.tracesortingformat import TraceSortingFormat class Gather: """ Gather mode. Internal. Provides the implementation for f.gather, reading n offsets from the intersection of two lines in a cube. """ def __init__(self, trace, iline, xline, offsets, sort): # cache constructed modes for performance self.trace = trace self.iline = iline self.xline = xline self.offsets = offsets self.sort = sort def _getindex(self, il, xl, offset, sorting): """ Get the trace index for an (il, xl, offset) tuple :rtype: int """ if sorting == TraceSortingFormat.INLINE_SORTING: xlind = self.xline._index(xl, None) return self.iline._index(il, offset) + xlind else: ilind = self.iline._index(il, None) return self.xline._index(xl, offset) + ilind def __getitem__(self, index): """ :rtype: iterator[numpy.ndarray]|numpy.ndarray """ if len(index) < 3: index = (index[0], index[1], slice(None)) il, xl, off = index sort = self.sort def isslice(x): return isinstance(x, slice) # gather[int,int,int] if not any(map(isslice, [il, xl, off])): return self.trace[self._getindex(il, xl, off, sort)] offs = off if isslice(off) else slice(off, off+1, 1) xs = list(filter(self.offsets.__contains__, range(*offs.indices(self.offsets[-1]+1)))) empty = np.empty(0, dtype = np.single) # gather[int,int,:] if not any(map(isslice, [il, xl])): if len(xs) == 0: return empty return tools.collect( self.trace[self._getindex(il, xl, x, sort)] for x in xs) # gather[:,:,:], gather[int,:,:], gather[:,int,:] # gather[:,:,int] etc def gen(): # precompute the xline number -> xline offset xlinds = { xlno: i for i, xlno in enumerate(self.xline.lines) } # doing range over gathers is VERY expensive, because every lookup # with a naive implementations would call _getindex to map lineno # -> trace index. However, ranges over gathers are done on a # by-line basis so lines can be buffered, and traces can be read # from the iline. This is the least efficient when there are very # few traces read per inline, but huge savings with larger subcubes last_il = self.iline.lines[-1] + 1 last_xl = self.xline.lines[-1] + 1 il_slice = il if isslice(il) else slice(il, il+1) xl_slice = xl if isslice(xl) else slice(xl, xl+1) il_range = range(*il_slice.indices(last_il)) xl_range = range(*xl_slice.indices(last_xl)) if not isslice(off): for iline in self.iline[il_slice, off]: for xlno in xl_range: yield iline[xlinds[xlno]] return if len(xs) == 0: for _, _ in itertools.product(ilrange, xlrange): yield empty return for ilno in il_range: iline = tools.collect(self.iline[ilno, off]) for x in xl_range: yield iline[:, xlinds[x]] return gen() segyio-1.5.2/python/segyio/version.py0000664000372000037200000000002213253720451017273 0ustar travistravisversion = '1.5.2' segyio-1.5.2/python/segyio/tracesortingformat.py0000664000372000037200000000020313253720451021524 0ustar travistravisfrom segyio import Enum class TraceSortingFormat(Enum): UNKNOWN_SORTING = 0 CROSSLINE_SORTING = 1 INLINE_SORTING = 2 segyio-1.5.2/python/segyio/_trace.py0000664000372000037200000000570513253720451017060 0ustar travistravistry: from future_builtins import zip except ImportError: pass import numpy as np from segyio._raw_trace import RawTrace class Trace: index_errmsg = "Trace {0} not in range [-{1},{1}]" def __init__(self, file): self._file = file """:type: segyio.file""" def __getitem__(self, index, buf=None): if isinstance(index, tuple): return self.__getitem__(index[0], index[1]) buf = self._trace_buffer(buf) if isinstance(index, slice): # always read the trace into a second buffer. This is to provide # exception safety: if an exception is raised and at least one # array has already been yielded to the caller, failing to read the # next trace won't make the already-returned array garbage def gen(): buf1 = buf buf2 = self._trace_buffer(None) for i in range(*index.indices(len(self))): buf1, buf2 = self._readtr(i, 1, 1, buf1, buf2) yield buf1 return gen() if not 0 <= abs(index) < len(self): raise IndexError(self.index_errmsg.format(index, len(self)-1)) # map negative a negative to the corresponding positive value start = (index + len(self)) % len(self) return self._readtr(start, 1, 1, buf)[0] def __setitem__(self, index, val): if isinstance(index, slice): for i, x in zip(range(*index.indices(len(self))), val): self.write_trace(i, x, self._file) return if not 0 <= abs(index) < len(self): raise IndexError(self.index_errmsg.format(index, len(self)-1)) self.write_trace(index, val, self._file) def __len__(self): return self._file.tracecount def __iter__(self): return self[:] def __repr__(self): return "Trace(traces = {}, samples = {})".format(len(self), self._file.samples) def _trace_buffer(self, buf=None): shape = self._file.samples.shape if buf is None: buf = np.empty(shape=shape, dtype=np.single) elif not isinstance(buf, np.ndarray): raise TypeError("Buffer must be None or numpy.ndarray") elif buf.dtype != np.single: buf = np.empty(shape=shape, dtype=np.single) return buf def _readtr(self, start, step, length, buf, buf1 = None): if buf1 is None: buf1 = buf buf1 = self._file.xfd.gettr(buf1, start, step, length) return buf1, buf @classmethod def write_trace(cls, traceno, buf, segy): """ :type traceno: int :type buf: ? :type segy: segyio.SegyFile """ if isinstance(buf, np.ndarray) and buf.dtype != np.single: raise TypeError("Numpy array must be of type single") segy.xfd.puttr(traceno, buf) @property def raw(self): """ :rtype: segyio.RawTrace """ return RawTrace(self) segyio-1.5.2/python/segyio/_raw_trace.py0000664000372000037200000000176613253720451017734 0ustar travistravisimport numpy as np try: xrange except NameError: pass else: range = xrange class RawTrace(object): def __init__(self, trace): self.trace = trace def __getitem__(self, index): """ :rtype: numpy.ndarray """ buf = None if isinstance(index, slice): f = self.trace._file start, stop, step = index.indices(f.tracecount) mstart, mstop = min(start, stop), max(start, stop) length = max(0, (mstop - mstart + (step - (1 if step > 0 else -1)))) buf = np.zeros(shape = (length, len(f.samples)), dtype = np.single) l = len(range(start, stop, step)) buf, _ = self.trace._readtr(start, step, l, buf) return buf if int(index) != index: raise TypeError("Trace index must be integer or slice.") buf = self.trace._trace_buffer(None) return self.trace._readtr(int(index), 1, 1, buf)[0] def __repr__(self): return self.trace.__repr__() + ".raw" segyio-1.5.2/python/segyio/tracefield.py0000664000372000037200000000462713253720451017727 0ustar travistravisfrom segyio import Enum class TraceField(Enum): TRACE_SEQUENCE_LINE = 1 TRACE_SEQUENCE_FILE = 5 FieldRecord = 9 TraceNumber = 13 EnergySourcePoint = 17 CDP = 21 CDP_TRACE = 25 TraceIdentificationCode = 29 NSummedTraces = 31 NStackedTraces = 33 DataUse = 35 offset = 37 ReceiverGroupElevation = 41 SourceSurfaceElevation = 45 SourceDepth = 49 ReceiverDatumElevation = 53 SourceDatumElevation = 57 SourceWaterDepth = 61 GroupWaterDepth = 65 ElevationScalar = 69 SourceGroupScalar = 71 SourceX = 73 SourceY = 77 GroupX = 81 GroupY = 85 CoordinateUnits = 89 WeatheringVelocity = 91 SubWeatheringVelocity = 93 SourceUpholeTime = 95 GroupUpholeTime = 97 SourceStaticCorrection = 99 GroupStaticCorrection = 101 TotalStaticApplied = 103 LagTimeA = 105 LagTimeB = 107 DelayRecordingTime = 109 MuteTimeStart = 111 MuteTimeEND = 113 TRACE_SAMPLE_COUNT = 115 TRACE_SAMPLE_INTERVAL = 117 GainType = 119 InstrumentGainConstant = 121 InstrumentInitialGain = 123 Correlated = 125 SweepFrequencyStart = 127 SweepFrequencyEnd = 129 SweepLength = 131 SweepType = 133 SweepTraceTaperLengthStart = 135 SweepTraceTaperLengthEnd = 137 TaperType = 139 AliasFilterFrequency = 141 AliasFilterSlope = 143 NotchFilterFrequency = 145 NotchFilterSlope = 147 LowCutFrequency = 149 HighCutFrequency = 151 LowCutSlope = 153 HighCutSlope = 155 YearDataRecorded = 157 DayOfYear = 159 HourOfDay = 161 MinuteOfHour = 163 SecondOfMinute = 165 TimeBaseCode = 167 TraceWeightingFactor = 169 GeophoneGroupNumberRoll1 = 171 GeophoneGroupNumberFirstTraceOrigField = 173 GeophoneGroupNumberLastTraceOrigField = 175 GapSize = 177 OverTravel = 179 CDP_X = 181 CDP_Y = 185 INLINE_3D = 189 CROSSLINE_3D = 193 ShotPoint = 197 ShotPointScalar = 201 TraceValueMeasurementUnit = 203 TransductionConstantMantissa = 205 TransductionConstantPower = 209 TransductionUnit = 211 TraceIdentifier = 213 ScalarTraceHeader = 215 SourceType = 217 SourceEnergyDirectionMantissa = 219 SourceEnergyDirectionExponent = 223 SourceMeasurementMantissa = 225 SourceMeasurementExponent = 229 SourceMeasurementUnit = 231 UnassignedInt1 = 233 UnassignedInt2 = 237segyio-1.5.2/python/segyio/binfield.py0000664000372000037200000000146313253720451017374 0ustar travistravisfrom segyio import Enum class BinField(Enum): JobID = 3201 LineNumber = 3205 ReelNumber = 3209 Traces = 3213 AuxTraces = 3215 Interval = 3217 IntervalOriginal = 3219 Samples = 3221 SamplesOriginal = 3223 Format = 3225 EnsembleFold = 3227 SortingCode = 3229 VerticalSum = 3231 SweepFrequencyStart = 3233 SweepFrequencyEnd = 3235 SweepLength = 3237 Sweep = 3239 SweepChannel = 3241 SweepTaperStart = 3243 SweepTaperEnd = 3245 Taper = 3247 CorrelatedTraces = 3249 BinaryGainRecovery = 3251 AmplitudeRecovery = 3253 MeasurementSystem = 3255 ImpulseSignalPolarity = 3257 VibratoryPolarity = 3259 Unassigned1 = 3261 SEGYRevision = 3501 TraceFlag = 3503 ExtendedHeaders = 3505 Unassigned2 = 3507 segyio-1.5.2/python/segyio/_field.py0000664000372000037200000000733313253720451017044 0ustar travistravisimport segyio from segyio import BinField from segyio import TraceField class Field(object): _bin_keys = [x for x in BinField.enums() if x != BinField.Unassigned1 and x != BinField.Unassigned2] _tr_keys = [x for x in TraceField.enums() if x != TraceField.UnassignedInt1 and x != TraceField.UnassignedInt2] def __init__(self, buf, write, field_type, traceno=None, keys = _tr_keys): self.buf = buf self.traceno = traceno self._field_type = field_type self._keys = keys self._write = write def _get_field(self, *args): return segyio._segyio.getfield(self.buf, *args) def _set_field(self, *args): return segyio._segyio.putfield(self.buf, *args) def __getitem__(self, field): # add some structure so we can always iterate over fields if isinstance(field, int) or isinstance(field, self._field_type): field = [field] d = {self._field_type(f): self._get_field(f) for f in field} # unpack the dictionary. if header[field] is requested, a # plain, unstructed output is expected, but header[f1,f2,f3] # yields a dict if len(d) == 1: return d.popitem()[1] return d def keys(self): return list(self._keys) def values(self): return map(self._get_field, self.keys()) def items(self): return zip(self.keys(), self.values()) def __contains__(self, key): return key in self._keys def __len__(self): return len(self._keys) def __iter__(self): return iter(self._keys) def __setitem__(self, field, val): self._set_field(field, val) self._write(self.buf, self.traceno) def update(self, value): if type(self) is type(value): buf = value.buf else: buf = self.buf # iter() on a dict only gives values, need key-value pairs try: value = value.items() except AttributeError: pass for k, v in value: self._set_field(int(k), v) self._write(buf, self.traceno) @classmethod def binary(cls, segy): try: buf = segy.xfd.getbin() except IOError: # the file was probably newly created and the binary header hasn't # been written yet. if this is the case we want to try and write # it. if the file was broken, permissions were wrong etc writing # will fail too buf = bytearray(segyio._segyio.binsize()) def wr(buf, *_): segy.xfd.putbin(buf) return Field(buf, write=wr, field_type=BinField, keys = Field._bin_keys) @classmethod def trace(cls, buf, traceno, segy): if traceno < 0: traceno = segy.tracecount + traceno if traceno >= segy.tracecount or traceno < 0: raise IndexError("Header out of range: 0 <= {} < {}".format(traceno, segy.tracecount)) if buf is None: buf = bytes(segyio._segyio.thsize()) try: segy.xfd.getth(traceno, buf) except IOError: # the file was probably newly created and the trace header hasn't # been written yet. if this is the case we want to try and write # it. if the file was broken, permissions were wrong etc writing # will fail too pass def wr(buf, traceno): segy.xfd.putth(traceno, buf) return Field(buf, traceno=traceno, write=wr, field_type=TraceField, keys=Field._tr_keys) def __repr__(self): return self[self.keys()].__repr__() segyio-1.5.2/python/segyio/segysampleformat.py0000664000372000037200000000041713253720451021200 0ustar travistravisfrom segyio import Enum class SegySampleFormat(Enum): IBM_FLOAT_4_BYTE = 1 SIGNED_INTEGER_4_BYTE = 2 SIGNED_SHORT_2_BYTE = 3 FIXED_POINT_WITH_GAIN_4_BYTE = 4 IEEE_FLOAT_4_BYTE = 5 NOT_IN_USE_1 = 6 NOT_IN_USE_2 = 7 SIGNED_CHAR_1_BYTE = 8 segyio-1.5.2/python/segyio/segyio.cpp0000664000372000037200000011716513253720451017260 0ustar travistravis#if defined(_DEBUG) && defined(_MSC_VER) # define _CRT_NOFORCE_MAINFEST 1 # undef _DEBUG # include # include # define _DEBUG 1 #else # include # include #endif #include "segyio/segy.h" #include #include #include #include #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif namespace { std::string segy_errstr( int err ) { std::stringstream ss; switch( err ) { case SEGY_OK: return "segyio.ok"; case SEGY_FOPEN_ERROR: return "segyio.fopen"; case SEGY_FSEEK_ERROR: return "segyio.fseek"; case SEGY_FREAD_ERROR: return "segyio.fread"; case SEGY_FWRITE_ERROR: return "segyio.fwrite"; case SEGY_INVALID_FIELD: return "segyio.invalid.field"; case SEGY_INVALID_SORTING: return "segyio.invalid.sorting"; case SEGY_MISSING_LINE_INDEX: return "segyio.missing.lineindex"; case SEGY_INVALID_OFFSETS: return "segyio.invalid.offsets"; case SEGY_TRACE_SIZE_MISMATCH: return "segyio.trace.size.mismatch"; case SEGY_INVALID_ARGS: return "segyio.invalid.args"; case SEGY_MMAP_ERROR: return "segyio.mmap.error"; case SEGY_MMAP_INVALID: return "segyio.mmap.invalid"; case SEGY_READONLY: return "segyio.readonly"; case SEGY_NOTFOUND: return "segyio.notfound"; default: ss << "code " << err << ""; return ss.str(); } } template< typename T1 > PyObject* TypeError( const char* msg, T1 t1 ) { return PyErr_Format( PyExc_TypeError, msg, t1 ); } PyObject* ValueError( const char* msg ) { PyErr_SetString( PyExc_ValueError, msg ); return NULL; } template< typename T1, typename T2 > PyObject* ValueError( const char* msg, T1 t1, T2 t2 ) { return PyErr_Format( PyExc_ValueError, msg, t1, t2 ); } PyObject* IndexError( const char* msg ) { PyErr_SetString( PyExc_IndexError, msg ); return NULL; } template< typename T1, typename T2 > PyObject* IndexError( const char* msg, T1 t1, T2 t2 ) { return PyErr_Format( PyExc_IndexError, msg, t1, t2 ); } template< typename T1, typename T2, typename T3 > PyObject* IndexError( const char* msg, T1 t1, T2 t2, T3 t3 ) { return PyErr_Format( PyExc_IndexError, msg, t1, t2, t3 ); } PyObject* BufferError( const char* msg ) { PyErr_SetString( PyExc_BufferError, msg ); return NULL; } PyObject* RuntimeError( const char* msg ) { PyErr_SetString( PyExc_RuntimeError, msg ); return NULL; } PyObject* RuntimeError( int err ) { const std::string msg = "uncaught exception: " + segy_errstr( err ); return RuntimeError( msg.c_str() ); } PyObject* IOErrno() { return PyErr_SetFromErrno( PyExc_IOError ); } PyObject* IOError( const char* msg ) { PyErr_SetString( PyExc_IOError, msg ); return NULL; } template< typename T1, typename T2 > PyObject* IOError( const char* msg, T1 t1, T2 t2 ) { return PyErr_Format( PyExc_IOError, msg, t1, t2 ); } template< typename T1 > PyObject* IOError( const char* msg, T1 t1 ) { return PyErr_Format( PyExc_IOError, msg, t1 ); } template< typename T1, typename T2 > PyObject* KeyError( const char* msg, T1 t1, T2 t2 ) { return PyErr_Format( PyExc_KeyError, msg, t1, t2 ); } PyObject* Error( int err ) { /* * a default error handler. The fseek errors are sufficiently described * with errno, and all cases that raise fwrite and fread errors get * sufficient context from stack trace to be generalised with a better * message. * * Anything else falls through to a generic RuntimeError "uncaught * exception" */ switch( err ) { case SEGY_FSEEK_ERROR: return IOErrno(); case SEGY_FWRITE_ERROR: // fallthrough case SEGY_FREAD_ERROR: return IOError( "I/O operation failed, " "likely corrupted file" ); case SEGY_READONLY: return IOError( "file not open for writing. " "open with 'r+'" ); default: return RuntimeError( err ); } } struct autofd { operator segy_file*() const; operator bool() const; void swap( autofd& other ); void close(); segy_file* fd; }; autofd::operator segy_file*() const { if( this->fd ) return this->fd; IOError( "I/O operation on closed file" ); return NULL; } autofd::operator bool() const { return this->fd; } void autofd::swap( autofd& other ) { std::swap( this->fd, other.fd ); } void autofd::close() { if( this->fd ) segy_close( this->fd ); this->fd = NULL; } struct segyiofd { PyObject_HEAD autofd fd; long trace0; int trace_bsize; int tracecount; int samplecount; int format; }; struct buffer_guard { /* automate Py_buffer handling. * * the python documentation does not mention any exception guarantees when * PyArg_ParseTuple, so assume that whenever the function fails, the buffer * object is either zero'd or untouched. That means checking if a * PyBuffer_Release should be called boils down to checking if underlying * buffer is a nullptr or not */ buffer_guard() { Py_buffer b = {}; this->buffer = b; } explicit buffer_guard( const Py_buffer& b ) : buffer( b ) {} buffer_guard( PyObject* o, int flags = PyBUF_CONTIG_RO ) { Py_buffer b = {}; this->buffer = b; if( !PyObject_CheckBuffer( o ) ) { TypeError( "'%s' does not expose buffer interface", o->ob_type->tp_name ); return; } const int cont = PyBUF_C_CONTIGUOUS; if( PyObject_GetBuffer( o, &this->buffer, flags | cont ) == 0 ) return; if( (flags & PyBUF_WRITABLE) == PyBUF_WRITABLE ) BufferError( "buffer must be contiguous and writable" ); else BufferError( "buffer must be contiguous and readable" ); } ~buffer_guard() { if( *this ) PyBuffer_Release( &this->buffer ); } operator bool() const { return this->buffer.buf; } Py_ssize_t len() const { return this->buffer.len; } Py_buffer* operator&() { return &this->buffer; } template< typename T > T* buf() const { return static_cast< T* >( this->buffer.buf ); } char* buf() const { return this->buf< char >(); } Py_buffer buffer; }; namespace fd { int init( segyiofd* self, PyObject* args, PyObject* ) { char* filename = NULL; char* mode = NULL; int tracecount = 0; buffer_guard buffer; if( !PyArg_ParseTuple( args, "ss|iz*", &filename, &mode, &tracecount, &buffer ) ) return -1; const char* binary = buffer.buf< const char >(); if( binary && buffer.len() < SEGY_BINARY_HEADER_SIZE ) { ValueError( "internal: binary buffer too small, expected %i, was %zd", SEGY_BINARY_HEADER_SIZE, buffer.len() ); return -1; } if( std::strlen( mode ) == 0 ) { ValueError( "mode string must be non-empty" ); return -1; } if( std::strlen( mode ) > 3 ) { ValueError( "invalid mode string '%s', good strings are %s", mode, "'r' (read-only) and 'r+' (read-write)" ); return -1; } struct unique : public autofd { explicit unique( segy_file* p ) { this->fd = p; } ~unique() { this->close(); } } fd( segy_open( filename, mode ) ); if( !fd && !strstr( "rb" "wb" "ab" "r+b" "w+b" "a+b", mode ) ) { ValueError( "invalid mode string '%s', good strings are %s", mode, "'r' (read-only) and 'r+' (read-write)" ); return -1; } if( !fd ) { IOErrno(); return -1; } char bin[ SEGY_BINARY_HEADER_SIZE ] = {}; if( !binary ) { const int err = segy_binheader( fd, bin ); if( err ) { Error( err ); return -1; } binary = bin; } const long trace0 = segy_trace0( binary ); const int samplecount = segy_samples( binary ); const int format = segy_format( binary ); const int trace_bsize = segy_trace_bsize( samplecount ); if( tracecount == 0 ) { const int err = segy_traces( fd, &tracecount, trace0, trace_bsize ); switch( err ) { case SEGY_OK: break; case SEGY_FSEEK_ERROR: IOErrno(); return -1; case SEGY_INVALID_ARGS: RuntimeError( "unable to count traces, " "no data traces past headers" ); return -1; case SEGY_TRACE_SIZE_MISMATCH: RuntimeError( "trace count inconsistent with file size, " "trace lengths possibly of non-uniform" ); return -1; default: Error( err ); return -1; } } /* * init can be called multiple times, which is treated as opening a new * file on the same object. That means the previous file handle must be * properly closed before the new file is set */ self->fd.swap( fd ); self->trace0 = trace0; self->trace_bsize = trace_bsize; self->format = format; self->samplecount = samplecount; self->tracecount = tracecount; return 0; } void dealloc( segyiofd* self ) { self->fd.close(); Py_TYPE( self )->tp_free( (PyObject*) self ); } PyObject* close( segyiofd* self ) { /* multiple close() is a no-op */ if( !self->fd ) return Py_BuildValue( "" ); errno = 0; self->fd.close(); if( errno ) return IOErrno(); return Py_BuildValue( "" ); } PyObject* flush( segyiofd* self ) { segy_file* fp = self->fd; if( !fp ) return NULL; errno = 0; segy_flush( self->fd, false ); if( errno ) return IOErrno(); return Py_BuildValue( "" ); } PyObject* mmap( segyiofd* self ) { segy_file* fp = self->fd; if( !fp ) return NULL; const int err = segy_mmap( fp ); if( err != SEGY_OK ) Py_RETURN_FALSE; Py_RETURN_TRUE; } /* * No C++11, so no std::vector::data. single-alloc automatic heap buffer, * without resize */ struct heapbuffer { explicit heapbuffer( int sz ) : ptr( new( std::nothrow ) char[ sz ] ) { if( !this->ptr ) { PyErr_SetString( PyExc_MemoryError, "unable to alloc buffer" ); return; } std::memset( this->ptr, 0, sz ); } ~heapbuffer() { delete[] this->ptr; } operator char*() { return this->ptr; } operator const char*() const { return this->ptr; } char* ptr; private: heapbuffer( const heapbuffer& ); }; PyObject* gettext( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int index = 0; if( !PyArg_ParseTuple( args, "i", &index ) ) return NULL; heapbuffer buffer( segy_textheader_size() ); if( !buffer ) return NULL; const int err = index == 0 ? segy_read_textheader( fp, buffer ) : segy_read_ext_textheader( fp, index - 1, buffer ); if( err ) return Error( err ); const size_t len = std::strlen( buffer ); return PyByteArray_FromStringAndSize( buffer, len ); } PyObject* puttext( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int index; buffer_guard buffer; if( !PyArg_ParseTuple( args, "is*", &index, &buffer ) ) return NULL; int size = std::min( int(buffer.len()), SEGY_TEXT_HEADER_SIZE ); heapbuffer buf( SEGY_TEXT_HEADER_SIZE ); if( !buf ) return NULL; const char* src = buffer.buf< const char >(); std::copy( src, src + size, buf.ptr ); const int err = segy_write_textheader( fp, index, buf ); if( err ) return Error( err ); return Py_BuildValue( "" ); } PyObject* getbin( segyiofd* self ) { segy_file* fp = self->fd; if( !fp ) return NULL; char buffer[ SEGY_BINARY_HEADER_SIZE ] = {}; const int err = segy_binheader( fp, buffer ); if( err ) return Error( err ); return PyByteArray_FromStringAndSize( buffer, sizeof( buffer ) ); } PyObject* putbin( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; buffer_guard buffer; if( !PyArg_ParseTuple(args, "s*", &buffer ) ) return NULL; if( buffer.len() < SEGY_BINARY_HEADER_SIZE ) return ValueError( "internal: binary buffer too small, " "expected %i, was %zd", SEGY_BINARY_HEADER_SIZE, buffer.len() ); const int err = segy_write_binheader( fp, buffer.buf< const char >() ); if( err == SEGY_INVALID_ARGS ) return IOError( "file not open for writing. open with 'r+'" ); if( err ) return Error( err ); return Py_BuildValue( "" ); } PyObject* getth( segyiofd* self, PyObject *args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int traceno; PyObject* bufferobj; if( !PyArg_ParseTuple( args, "iO", &traceno, &bufferobj ) ) return NULL; buffer_guard buffer( bufferobj, PyBUF_CONTIG ); if( !buffer ) return NULL; if( buffer.len() < SEGY_TRACE_HEADER_SIZE ) return ValueError( "internal: trace header buffer too small, " "expected %i, was %zd", SEGY_TRACE_HEADER_SIZE, buffer.len() ); int err = segy_traceheader( fp, traceno, buffer.buf(), self->trace0, self->trace_bsize ); switch( err ) { case SEGY_OK: Py_INCREF( bufferobj ); return bufferobj; case SEGY_FREAD_ERROR: return IOError( "I/O operation failed on trace header %d", traceno ); default: return Error( err ); } } PyObject* putth( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int traceno; buffer_guard buf; if( !PyArg_ParseTuple( args, "is*", &traceno, &buf ) ) return NULL; if( buf.len() < SEGY_TRACE_HEADER_SIZE ) return ValueError( "internal: trace header buffer too small, " "expected %i, was %zd", SEGY_TRACE_HEADER_SIZE, buf.len() ); const char* buffer = buf.buf< const char >(); const int err = segy_write_traceheader( fp, traceno, buffer, self->trace0, self->trace_bsize ); switch( err ) { case SEGY_OK: return Py_BuildValue( "" ); case SEGY_FWRITE_ERROR: return IOError( "I/O operation failed on trace header %d", traceno ); default: return Error( err ); } } PyObject* field_forall( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; PyObject* bufferobj; int start, stop, step; int field; if( !PyArg_ParseTuple( args, "Oiiii", &bufferobj, &start, &stop, &step, &field ) ) return NULL; if( step == 0 ) return ValueError( "slice step cannot be zero" ); buffer_guard buffer( bufferobj, PyBUF_CONTIG ); if( !buffer ) return NULL; const int err = segy_field_forall( fp, field, start, stop, step, buffer.buf< int >(), self->trace0, self->trace_bsize ); if( err ) return Error( err ); Py_INCREF( bufferobj ); return bufferobj; } PyObject* field_foreach( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; PyObject* bufferobj; buffer_guard indices; int field; if( !PyArg_ParseTuple( args, "Os*i", &bufferobj, &indices, &field ) ) return NULL; buffer_guard bufout( bufferobj, PyBUF_CONTIG ); if( !bufout ) return NULL; if( bufout.len() != indices.len() ) return ValueError( "internal: array size mismatch " "(output %zd, indices %zd)", bufout.len(), indices.len() ); const int* ind = indices.buf< const int >(); int* out = bufout.buf< int >(); Py_ssize_t len = bufout.len() / sizeof(int); int err = 0; for( int i = 0; err == 0 && i < len; ++i ) { err = segy_field_forall( fp, field, ind[ i ], ind[ i ] + 1, 1, out + i, self->trace0, self->trace_bsize ); } if( err ) return Error( err ); Py_INCREF( bufferobj ); return bufferobj; } PyObject* metrics( segyiofd* self ) { static const int text = SEGY_TEXT_HEADER_SIZE; static const int bin = SEGY_BINARY_HEADER_SIZE; const int ext = (self->trace0 - (text + bin)) / text; return Py_BuildValue( "{s:i, s:l, s:i, s:i, s:i, s:i}", "tracecount", self->tracecount, "trace0", self->trace0, "trace_bsize", self->trace_bsize, "samplecount", self->samplecount, "format", self->format, "ext_headers", ext ); } struct metrics_errmsg { int il, xl, of; PyObject* operator()( int err ) const { switch( err ) { case SEGY_INVALID_FIELD: return IndexError( "invalid iline, (%i), xline (%i), " "or offset (%i) field", il, xl, of ); case SEGY_INVALID_SORTING: return RuntimeError( "unable to find sorting." ); default: return Error( err ); } } }; PyObject* cube_metrics( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int il; int xl; if( !PyArg_ParseTuple( args, "ii", &il, &xl ) ) return NULL; metrics_errmsg errmsg = { il, xl, SEGY_TR_OFFSET }; int sorting = -1; int err = segy_sorting( fp, il, xl, SEGY_TR_OFFSET, &sorting, self->trace0, self->trace_bsize ); if( err ) return errmsg( err ); int offset_count = -1; err = segy_offsets( fp, il, xl, self->tracecount, &offset_count, self->trace0, self->trace_bsize ); if( err ) return errmsg( err ); int xl_count = 0; int il_count = 0; err = segy_lines_count( fp, il, xl, sorting, offset_count, &il_count, &xl_count, self->trace0, self->trace_bsize ); if( err == SEGY_NOTFOUND ) return ValueError( "could not parse geometry, " "open with strict=False" ); if( err ) return errmsg( err ); return Py_BuildValue( "{s:i, s:i, s:i, s:i, s:i, s:i, s:i}", "sorting", sorting, "iline_field", il, "xline_field", xl, "offset_field", 37, "offset_count", offset_count, "iline_count", il_count, "xline_count", xl_count ); } long getitem( PyObject* dict, const char* key ) { return PyLong_AsLong( PyDict_GetItemString( dict, key ) ); } PyObject* indices( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; PyObject* metrics; buffer_guard iline_out; buffer_guard xline_out; buffer_guard offset_out; if( !PyArg_ParseTuple( args, "O!w*w*w*", &PyDict_Type, &metrics, &iline_out, &xline_out, &offset_out ) ) return NULL; const int iline_count = getitem( metrics, "iline_count" ); const int xline_count = getitem( metrics, "xline_count" ); const int offset_count = getitem( metrics, "offset_count" ); if( iline_out.len() < Py_ssize_t(iline_count * sizeof( int )) ) return ValueError( "internal: inline indices buffer too small, " "expected %i, was %zd", iline_count, iline_out.len() ); if( xline_out.len() < Py_ssize_t(xline_count * sizeof( int )) ) return ValueError( "internal: crossline indices buffer too small, " "expected %i, was %zd", xline_count, xline_out.len() ); if( offset_out.len() < Py_ssize_t(offset_count * sizeof( int )) ) return ValueError( "internal: offset indices buffer too small, " "expected %i, was %zd", offset_count, offset_out.len() ); const int il_field = getitem( metrics, "iline_field" ); const int xl_field = getitem( metrics, "xline_field" ); const int offset_field = getitem( metrics, "offset_field" ); const int sorting = getitem( metrics, "sorting" ); if( PyErr_Occurred() ) return NULL; metrics_errmsg errmsg = { il_field, xl_field, SEGY_TR_OFFSET }; int err = segy_inline_indices( fp, il_field, sorting, iline_count, xline_count, offset_count, iline_out.buf< int >(), self->trace0, self->trace_bsize ); if( err ) return errmsg( err ); err = segy_crossline_indices( fp, xl_field, sorting, iline_count, xline_count, offset_count, xline_out.buf< int >(), self->trace0, self->trace_bsize ); if( err ) return errmsg( err ); err = segy_offset_indices( fp, offset_field, offset_count, offset_out.buf< int >(), self->trace0, self->trace_bsize ); if( err ) return errmsg( err ); return Py_BuildValue( "" ); } PyObject* gettr( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; PyObject* bufferobj; int start, length, step; if( !PyArg_ParseTuple( args, "Oiii", &bufferobj, &start, &step, &length ) ) return NULL; buffer_guard buffer( bufferobj, PyBUF_CONTIG ); if( !buffer) return NULL; const int samples = self->samplecount; const long long bufsize = (long long) length * samples; const long trace0 = self->trace0; const int trace_bsize = self->trace_bsize; if( buffer.len() < bufsize ) return ValueError( "internal: data trace buffer too small, " "expected %zi, was %zd", bufsize, buffer.len() ); int err = 0; int i = 0; float* buf = buffer.buf< float >(); for( ; err == 0 && i < length; ++i, buf += samples ) { err = segy_readtrace( fp, start + (i * step), buf, trace0, trace_bsize ); } if( err == SEGY_FREAD_ERROR ) return IOError( "I/O operation failed on data trace %d", i ); if( err ) return Error( err ); segy_to_native( self->format, bufsize, buffer.buf< float >() ); Py_INCREF( bufferobj ); return bufferobj; } PyObject* puttr( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int traceno; float* buffer; Py_ssize_t buflen; if( !PyArg_ParseTuple( args, "is#", &traceno, &buffer, &buflen ) ) return NULL; segy_from_native( self->format, self->samplecount, buffer ); int err = segy_writetrace( fp, traceno, buffer, self->trace0, self->trace_bsize ); segy_to_native( self->format, self->samplecount, buffer ); switch( err ) { case SEGY_OK: return Py_BuildValue(""); case SEGY_FREAD_ERROR: return IOError( "I/O operation failed on data trace %d", traceno ); default: return Error( err ); } } PyObject* getline( segyiofd* self, PyObject* args) { segy_file* fp = self->fd; if( !fp ) return NULL; int line_trace0; int line_length; int stride; int offsets; PyObject* bufferobj; if( !PyArg_ParseTuple( args, "iiiiO", &line_trace0, &line_length, &stride, &offsets, &bufferobj ) ) return NULL; buffer_guard buffer( bufferobj, PyBUF_CONTIG ); if( !buffer ) return NULL; int err = segy_read_line( fp, line_trace0, line_length, stride, offsets, buffer.buf< float >(), self->trace0, self->trace_bsize); if( err ) return Error( err ); segy_to_native( self->format, self->samplecount * line_length, buffer.buf< float >() ); Py_INCREF( bufferobj ); return bufferobj; } PyObject* getdepth( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int depth; int count; int offsets; PyObject* bufferobj; if( !PyArg_ParseTuple( args, "iiiO", &depth, &count, &offsets, &bufferobj ) ) return NULL; buffer_guard buffer( bufferobj, PyBUF_CONTIG ); if( !buffer ) return NULL; int traceno = 0; int err = 0; float* buf = buffer.buf< float >(); const long trace0 = self->trace0; const int trace_bsize = self->trace_bsize; for( ; err == 0 && traceno < count; ++traceno) { err = segy_readsubtr( fp, traceno * offsets, depth, depth + 1, 1, buf++, NULL, trace0, trace_bsize); } if( err == SEGY_FREAD_ERROR ) return IOError( "I/O operation failed on data trace %d at depth %d", traceno, depth ); if( err ) return Error( err ); segy_to_native( self->format, count, buffer.buf< float >() ); Py_INCREF( bufferobj ); return bufferobj; } PyObject* getdt( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; float fallback; if( !PyArg_ParseTuple(args, "f", &fallback ) ) return NULL; float dt; int err = segy_sample_interval( fp, fallback, &dt ); if( err == SEGY_OK ) return PyFloat_FromDouble( dt ); if( err != SEGY_FREAD_ERROR && err != SEGY_FSEEK_ERROR ) return Error( err ); /* * Figure out if the problem is reading the trace header * or the binary header */ char buffer[ SEGY_BINARY_HEADER_SIZE ]; err = segy_binheader( fp, buffer ); if( err ) return IOError( "I/O operation failed on binary header, " "likely corrupted file" ); err = segy_traceheader( fp, 0, buffer, self->trace0, self->trace_bsize ); if( err == SEGY_FREAD_ERROR ) return IOError( "I/O operation failed on trace header 0, " "likely corrupted file" ); return Error( err ); } PyObject* rotation( segyiofd* self, PyObject* args ) { segy_file* fp = self->fd; if( !fp ) return NULL; int line_length; int stride; int offsets; buffer_guard linenos; if( !PyArg_ParseTuple( args, "iiis*", &line_length, &stride, &offsets, &linenos ) ) return NULL; float rotation; int err = segy_rotation_cw( fp, line_length, stride, offsets, linenos.buf< const int >(), linenos.len() / sizeof( int ), &rotation, self->trace0, self->trace_bsize ); if( err ) return Error( err ); return PyFloat_FromDouble( rotation ); } PyMethodDef methods [] = { { "close", (PyCFunction) fd::close, METH_VARARGS, "Close file." }, { "flush", (PyCFunction) fd::flush, METH_VARARGS, "Flush file." }, { "mmap", (PyCFunction) fd::mmap, METH_NOARGS, "mmap file." }, { "gettext", (PyCFunction) fd::gettext, METH_VARARGS, "Get text header." }, { "puttext", (PyCFunction) fd::puttext, METH_VARARGS, "Put text header." }, { "getbin", (PyCFunction) fd::getbin, METH_VARARGS, "Get binary header." }, { "putbin", (PyCFunction) fd::putbin, METH_VARARGS, "Put binary header." }, { "getth", (PyCFunction) fd::getth, METH_VARARGS, "Get trace header." }, { "putth", (PyCFunction) fd::putth, METH_VARARGS, "Put trace header." }, { "field_forall", (PyCFunction) fd::field_forall, METH_VARARGS, "Field for-all." }, { "field_foreach", (PyCFunction) fd::field_foreach, METH_VARARGS, "Field for-each." }, { "gettr", (PyCFunction) fd::gettr, METH_VARARGS, "Get trace." }, { "puttr", (PyCFunction) fd::puttr, METH_VARARGS, "Put trace." }, { "getline", (PyCFunction) fd::getline, METH_VARARGS, "Get line." }, { "getdepth", (PyCFunction) fd::getdepth, METH_VARARGS, "Get depth." }, { "getdt", (PyCFunction) fd::getdt, METH_VARARGS, "Get sample interval (dt)." }, { "rotation", (PyCFunction) fd::rotation, METH_VARARGS, "Get clockwise rotation." }, { "metrics", (PyCFunction) fd::metrics, METH_NOARGS, "Metrics." }, { "cube_metrics", (PyCFunction) fd::cube_metrics, METH_VARARGS, "Cube metrics." }, { "indices", (PyCFunction) fd::indices, METH_VARARGS, "Indices." }, { NULL } }; } PyTypeObject Segyiofd = { PyVarObject_HEAD_INIT( NULL, 0 ) "_segyio.segyfd", /* name */ sizeof( segyiofd ), /* basic size */ 0, /* tp_itemsize */ (destructor)fd::dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "segyio file descriptor", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ fd::methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)fd::init, /* tp_init */ }; PyObject* binsize( PyObject* ) { return PyLong_FromLong( segy_binheader_size() ); } PyObject* thsize( PyObject* ) { return PyLong_FromLong( SEGY_TRACE_HEADER_SIZE ); } PyObject* textsize(PyObject* ) { return PyLong_FromLong( SEGY_TEXT_HEADER_SIZE ); } PyObject* trbsize( PyObject*, PyObject* args ) { int sample_count; if( !PyArg_ParseTuple( args, "i", &sample_count ) ) return NULL; return PyLong_FromLong( segy_trace_bsize( sample_count ) ); } PyObject* getfield( PyObject*, PyObject *args ) { buffer_guard buffer; int field; if( !PyArg_ParseTuple( args, "s*i", &buffer, &field ) ) return NULL; if( buffer.len() != SEGY_BINARY_HEADER_SIZE && buffer.len() != SEGY_TRACE_HEADER_SIZE ) return BufferError( "buffer too small" ); int value = 0; int err = buffer.len() == segy_binheader_size() ? segy_get_bfield( buffer.buf< const char >(), field, &value ) : segy_get_field( buffer.buf< const char >(), field, &value ) ; switch( err ) { case SEGY_OK: return PyLong_FromLong( value ); case SEGY_INVALID_FIELD: return IndexError( "invalid field value" ); default: return Error( err ); } } PyObject* putfield( PyObject*, PyObject *args ) { buffer_guard buffer; int field; int value; if( !PyArg_ParseTuple( args, "w*ii", &buffer, &field, &value ) ) return NULL; if( buffer.len() != SEGY_BINARY_HEADER_SIZE && buffer.len() != SEGY_TRACE_HEADER_SIZE ) return BufferError( "buffer too small" ); int err = buffer.len() == segy_binheader_size() ? segy_set_bfield( buffer.buf< char >(), field, value ) : segy_set_field( buffer.buf< char >(), field, value ) ; switch( err ) { case SEGY_OK: return PyLong_FromLong( value ); case SEGY_INVALID_FIELD: return IndexError( "invalid field value" ); default: return Error( err ); } } PyObject* line_metrics( PyObject*, PyObject *args) { SEGY_SORTING sorting; int trace_count; int inline_count; int crossline_count; int offset_count; if( !PyArg_ParseTuple( args, "iiiii", &sorting, &trace_count, &inline_count, &crossline_count, &offset_count ) ) return NULL; int iline_length = segy_inline_length( crossline_count ); int xline_length = segy_crossline_length( inline_count ); int iline_stride = 0; int err = segy_inline_stride( sorting, inline_count, &iline_stride ); // only check first call since the only error that can occur is // SEGY_INVALID_SORTING if( err == SEGY_INVALID_SORTING ) return ValueError( "internal: invalid sorting." ); if( err ) return Error( err ); int xline_stride; segy_crossline_stride( sorting, crossline_count, &xline_stride ); return Py_BuildValue( "{s:i, s:i, s:i, s:i}", "xline_length", xline_length, "xline_stride", xline_stride, "iline_length", iline_length, "iline_stride", iline_stride ); } PyObject* fread_trace0( PyObject* , PyObject* args ) { int lineno; int other_line_length; int stride; int offsets; int* indices; int indiceslen; char* linetype; if( !PyArg_ParseTuple( args, "iiiis#s", &lineno, &other_line_length, &stride, &offsets, &indices, &indiceslen, &linetype ) ) return NULL; int trace_no = 0; int err = segy_line_trace0( lineno, other_line_length, stride, offsets, indices, indiceslen / sizeof( int ), &trace_no ); if( err == SEGY_MISSING_LINE_INDEX ) return KeyError( "no such %s %d", linetype, lineno ); if( err ) return Error( err ); return PyLong_FromLong( trace_no ); } PyObject* format( PyObject* , PyObject* args ) { PyObject *out; int format; if( !PyArg_ParseTuple( args, "Oi", &out, &format ) ) return NULL; buffer_guard buffer( out, PyBUF_CONTIG );; const int len = buffer.len() / sizeof( float ); segy_to_native( format, len, buffer.buf< float >() ); Py_INCREF( out ); return out; } PyMethodDef SegyMethods[] = { { "binsize", (PyCFunction) binsize, METH_NOARGS, "Size of the binary header." }, { "thsize", (PyCFunction) thsize, METH_NOARGS, "Size of the trace header." }, { "textsize", (PyCFunction) textsize, METH_NOARGS, "Size of the text header." }, { "trace_bsize", (PyCFunction) trbsize, METH_VARARGS, "Size of a trace (in bytes)." }, { "getfield", (PyCFunction) getfield, METH_VARARGS, "Get a header field." }, { "putfield", (PyCFunction) putfield, METH_VARARGS, "Put a header field." }, { "line_metrics", (PyCFunction) line_metrics, METH_VARARGS, "Find the length and stride of lines." }, { "fread_trace0", (PyCFunction) fread_trace0, METH_VARARGS, "Find trace0 of a line." }, { "native", (PyCFunction) format, METH_VARARGS, "Convert to native float." }, { NULL } }; } /* module initialization */ #ifdef IS_PY3K static struct PyModuleDef segyio_module = { PyModuleDef_HEAD_INIT, "_segyio", /* name of module */ NULL, /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ SegyMethods }; PyMODINIT_FUNC PyInit__segyio(void) { Segyiofd.tp_new = PyType_GenericNew; if( PyType_Ready( &Segyiofd ) < 0 ) return NULL; PyObject* m = PyModule_Create(&segyio_module); if( !m ) return NULL; Py_INCREF( &Segyiofd ); PyModule_AddObject( m, "segyiofd", (PyObject*)&Segyiofd ); return m; } #else PyMODINIT_FUNC init_segyio(void) { Segyiofd.tp_new = PyType_GenericNew; if( PyType_Ready( &Segyiofd ) < 0 ) return; PyObject* m = Py_InitModule("_segyio", SegyMethods); Py_INCREF( &Segyiofd ); PyModule_AddObject( m, "segyiofd", (PyObject*)&Segyiofd ); } #endif segyio-1.5.2/multibuild/0000775000372000037200000000000013253720451014574 5ustar travistravissegyio-1.5.2/config.sh0000664000372000037200000000165013253720451014225 0ustar travistravis#!/bin/sh function run_tests { set -x python -c "import segyio; print(segyio.__version__)" python ../python/examples/scan_min_max.py ../test-data/small.sgy } function pre_build { if [ -n "$IS_OSX" ]; then return; fi if [ -d build-centos5 ]; then return; fi # the cmakes available in yum for centos5 are too old (latest 2.11.x), so # do a dirty hack and get a pre-compiled i386-binary from cmake.org and run # it. it's only necessary in the multilinux docker container and hopefully # only until multilinux2 images are released mkdir build-centos5 pushd build-centos5 export cmake=cmake-2.8.12.2-Linux-i386 wget --no-check-certificate https://cmake.org/files/v2.8/cmake-2.8.12.2-Linux-i386.tar.gz tar xzvf $cmake.tar.gz ./$cmake/bin/cmake --version ./$cmake/bin/cmake .. -DBUILD_PYTHON=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON make install popd } segyio-1.5.2/appveyor.yml0000664000372000037200000000530313253720451015013 0ustar travistravisversion: 1.0.{build} clone_depth: 1 configuration: - Release os: Visual Studio 2015 image: Visual Studio 2015 platform: - x86 - x64 environment: MEX: -DBUILD_MEX=OFF INSTALL_DIR: C:\projects\segyio-install TWINE_PASSWORD: secure: kTga6+rLL1fNpKqFNcX0QZNyqiYaHsEfSS4k6wvUoTE= matrix: - PYTHON: C:\Python27 LANG: -DBUILD_PYTHON=ON PYTHON_VERSION: 27 - PYTHON: C:\Python35 LANG: -DBUILD_PYTHON=ON PYTHON_VERSION: 35 - PYTHON: C:\Python36 LANG: -DBUILD_PYTHON=ON PYTHON_VERSION: 36 matrix: fast_finish: true install: - git submodule update --init --recursive - IF DEFINED PYTHON (IF "%platform%" == "x64" SET PYTHON=%PYTHON%-x64) - IF DEFINED PYTHON SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% - IF DEFINED PYTHON pip install -r requirements.txt - IF DEFINED PYTHON pip install twine wheel pytest before_build: - IF DEFINED PYTHON SET LANG=%LANG% -DPYTHON_EXECUTABLE=%PYTHON%\python - git fetch --tags # python extensions with setup.py typically requires building with the same # compiler as python itself, but it seems that the extern C does a reasonable # job of keeping it binary compatible. For python < 3.0, copy the shared lib # into the build directory (because otherwise visual c++ 9.0 is invoked, and it # supports no C99. For newer pythons, setup.py is capable of building segyio build_script: - cmake --version - IF "%platform%" == "x64" set W64="-GVisual Studio 14 2015 Win64" - mkdir build - ps: pushd build - cmake %APPVEYOR_BUILD_FOLDER% %W64% %MEX% %LANG% -DCMAKE_INSTALL_PREFIX=%INSTALL_DIR% - cmake --build . --config "%configuration%" --target install - ctest -C "%configuration%" --output-on-failure - ps: popd - ps: pushd build/python - IF DEFINED PYTHON python %APPVEYOR_BUILD_FOLDER%/setup.py bdist_wheel - ps: popd test_script: - set PATH=%INSTALL_DIR%\bin;%PATH% - ps: pushd build/python - IF DEFINED PYTHON FOR /F "tokens=*" %%G IN ('dir /b dist\*.whl') DO pip install dist/%%G - ps: popd - IF DEFINED PYTHON python -c "import segyio;f=segyio.open('test-data/small.sgy');print(f.ilines)" - IF DEFINED PYTHON python python/examples/scan_min_max.py test-data/small.sgy before_deploy: - ps: pushd build/python after_deploy: - ps: popd deploy_script: - ps: | Write-Host "TAG: $env:APPVEYOR_REPO_TAG" If ($env:APPVEYOR_REPO_TAG -eq "false") { return } Write-Host "Uploading wheels to pypi" Invoke-Expression "twine upload -u statoil-travis --skip-existing dist/*" If ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } segyio-1.5.2/bandit.yml0000664000372000037200000000001713253720451014404 0ustar travistravisskips: ['B101']segyio-1.5.2/.travis.yml0000664000372000037200000001420413253720451014534 0ustar travistravislanguage: python python: - 2.7 - 3.4 - 3.5 - 3.6 env: global: - UNICODE_WIDTH=32 - PLAT=x86_64 - MB_PYTHON_VERSION=$TRAVIS_PYTHON_VERSION - VERSION=$(echo $TRAVIS_TAG | sed s/v//) os: - linux services: docker sudo: required matrix: fast_finish: true include: - os: linux compiler: clang python: 3.6 env: - SCAN="scan-build --status-bugs" - SOURCEDEPLOY=1 - os: osx language: generic env: MB_PYTHON_VERSION=2.7 python: 2.7 - os: osx language: generic env: MB_PYTHON_VERSION=3.4 python: 3.4 - os: osx language: generic env: MB_PYTHON_VERSION=3.5 python: 3.5 - os: osx language: generic env: MB_PYTHON_VERSION=3.6 python: 3.6 addons: apt: sources: - sourceline: 'ppa:jokva/backports' - george-edison55-precise-backports # cmake 3 packages: - valgrind - cppcheck - cmake - cmake-data before_install: - unset -f pushd - unset -f popd - source multibuild/common_utils.sh - source multibuild/travis_steps.sh - before_install install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update && brew install cppcheck; fi - pip install -r requirements.txt - pip install bandit setuptools setuptools-scm pytest before_script: - enabled="-DBUILD_PYTHON=OFF -DBUILD_MEX=OFF" - if [[ -n "${MB_PYTHON_VERSION+1}" ]]; then enabled="$enabled -DBUILD_PYTHON=ON -DREQUIRE_PYTHON=ON"; fi - bandit -c bandit.yml -r python - cmake --version - mkdir build - pushd build - $SCAN cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_NAME_DIR=/usr/local/lib $enabled .. - cppcheck --enable=style,portability,performance,warning --library=posix --library=$TRAVIS_BUILD_DIR/cppcheck/segyio.cfg --suppressions-list=$TRAVIS_BUILD_DIR/cppcheck/suppressions.txt --inline-suppr --project=compile_commands.json --error-exitcode=1 - popd # distutils/setuptools on macos ignores the --rpath argument, so set # DYLD_LIBRARY_PATH so that the freshly-built image is picked up on for the # tests. # # segyio is configured with CMAKE_INSTALL_NAME_DIR so that the wheel build on # OS X so the delocate tool can figure out what dylib to include. script: - pushd build - export DYLD_LIBRARY_PATH=$PWD/lib - $SCAN make && ctest --output-on-failure - sudo make install - popd - build_wheel . $PLAT - install_run $PLAT - if [[ -z $SOURCEDEPLOY ]]; then mv wheelhouse dist; fi before_deploy: # OS X ships with a tar that doesn't support --exclude-vcs, which is # unproblematic as long as source code is uploaded from linux. - if [[ "$TRAVIS_OS_NAME" == "linux" && ! -d segyio-$VERSION ]]; then git clone $TRAVIS_BUILD_DIR segyio-$VERSION; echo "version = '$TRAVIS_TAG'" > segyio-$VERSION/python/segyio/version.py; tar --exclude-vcs -C $TRAVIS_BUILD_DIR -czvf segyio-$VERSION.tar.gz segyio-$VERSION; zip --exclude *.git -r segyio-$VERSION.zip $TRAVIS_BUILD_DIR/segyio-$VERSION/*; fi deploy: - provider: pypi # source distribution (done from linux python 3.6 only) skip_cleanup: true skip_upload_docs: true user: statoil-travis distributions: sdist password: secure: WHeA5x2iiXzjYXdgZeJU6l4fawRi/umqH1713QAj0RPZBAnD+9m8Zrpn2UWn9+1dtx6xUMkNg3ZTueVKTKo0f2i/4o0xkzQ5BW11cetCWusV2Dku1btPTA5Fhs+dvPDlL3m496a3Bq/A/fRDj5JLDiiPibvpM01lEBxFKYooWGQ75HVuhnAt57vabD45gDpIE7N23+So4+9bsG/nT/ZlgoaS01uLTdlnf6tjNgP0/UBkonmedC62iVvCu7itfHZMTY2rSeww7KBMI3s7Gz+cyx9IbI3shbDpdJGHpM8Qe+1oFi31Z+DylWQA5SkpHlLMUP+zjMKLF+1hXGNUeJuyadIFrxzbS3vTV2yRPa6ol8q+bX7YLy0xNLSE8aMm54LKgXfRKLc3G8d3I33oEfyk5hygY8iEX728r2TsARslYxOF3sZqJvY8lx4GBEDiXxX2GJvGCPy/Uby786uWnZlFFkDERk3nawE0W68zY0GElbutq6HMSk6v49J7em9Rg/QLptjoa0uF2A9Cy+BAJcnfauIawOG3UPXZ8kD60PTbu8tqtwtXO5lzTSIOr1lt3+6R8GmCc2hz2YXELKvdnZWYCXAjnNuC4eESlp7zxmzhpOVkb2Jy1v6rGKpcxVMggLQbOjOGWPLg9q4+OZQBw5bhMGmVGQhSBuxzEb/wBRy9XV8= on: condition: $SOURCEDEPLOY tags: true - provider: pypi # upload built wheels skip_cleanup: true skip_upload_docs: true user: statoil-travis distributions: build password: secure: WHeA5x2iiXzjYXdgZeJU6l4fawRi/umqH1713QAj0RPZBAnD+9m8Zrpn2UWn9+1dtx6xUMkNg3ZTueVKTKo0f2i/4o0xkzQ5BW11cetCWusV2Dku1btPTA5Fhs+dvPDlL3m496a3Bq/A/fRDj5JLDiiPibvpM01lEBxFKYooWGQ75HVuhnAt57vabD45gDpIE7N23+So4+9bsG/nT/ZlgoaS01uLTdlnf6tjNgP0/UBkonmedC62iVvCu7itfHZMTY2rSeww7KBMI3s7Gz+cyx9IbI3shbDpdJGHpM8Qe+1oFi31Z+DylWQA5SkpHlLMUP+zjMKLF+1hXGNUeJuyadIFrxzbS3vTV2yRPa6ol8q+bX7YLy0xNLSE8aMm54LKgXfRKLc3G8d3I33oEfyk5hygY8iEX728r2TsARslYxOF3sZqJvY8lx4GBEDiXxX2GJvGCPy/Uby786uWnZlFFkDERk3nawE0W68zY0GElbutq6HMSk6v49J7em9Rg/QLptjoa0uF2A9Cy+BAJcnfauIawOG3UPXZ8kD60PTbu8tqtwtXO5lzTSIOr1lt3+6R8GmCc2hz2YXELKvdnZWYCXAjnNuC4eESlp7zxmzhpOVkb2Jy1v6rGKpcxVMggLQbOjOGWPLg9q4+OZQBw5bhMGmVGQhSBuxzEb/wBRy9XV8= on: condition: -z $SOURCEDEPLOY tags: true - provider: releases # upload tarball and zip with source code skip_cleanup: true overwrite: true file: - $TRAVIS_BUILD_DIR/segyio-$VERSION.tar.gz - $TRAVIS_BUILD_DIR/segyio-$VERSION.zip api_key: secure: VdV/5kCCwUFwgU7wvKRrOy4u9nnvLsM3RoW1G2z8w8e838fcTeXLU3Zu8rLzLpcaXJKX07WNQmG85PZrgRjB9mzZAbXnPJeMk5MT3YVWaiyF6zNxK+mQQOvTCJ8P25nm5Iq1PLtIW/wM1RqNLgT8t3AsLiIva+6JK+2wGA2ilOl2YVgJqEiZV1f9DJutPewDf8pqYrnrHktMmkio/uubc12MLFOrNeQxK/EJahw7x56q8EcxrJXHnv6T2zEvUdcEYfRvIrT62jiVqiJP9wmUVdq2x8/RlgRzcCpGhy2o17iMUIH228so2hbu5/NcvtAh/LUDeqlhQ1YLRp1vO6H8Uh9B6aJgBNPwYmrKBHQQD27l5vt74DNJuqsXTZtrQlzUiiptKTV7mjFckF7mG04d1J/0GcVcxjpRZa3rp85KymQKWTsuWJYrCWSeVh+SMwUAbMZ3/vhEKPtrf4rwI+4INTitLuBNfcyH0W+W/rWLBMYoojRW0NJjc+4HJGpkMPbFkDjpde31ZqgI/Cec0GPpqbtsb3DkK/f4mLrR+F59b3+Lj0Cjyh9lM2fVXTrPYt13kdwbtBnNy6x2kdTkEBCk9m+eTFAfJGal+3SwgLeiGQdkjdZ7vSLwwdRf7Qvz5WjxKQ+feFWm+qi0GurWOEaZrubB82CGukxOXKzrvAGN3Eo= on: tags: true condition: $SOURCEDEPLOY segyio-1.5.2/setup.cfg0000664000372000037200000000010513253720451014237 0ustar travistravis[aliases] test=pytest [tool:pytest] python_files = python/test/*.py segyio-1.5.2/external/0000775000372000037200000000000013253720451014244 5ustar travistravissegyio-1.5.2/external/catch2/0000775000372000037200000000000013253720451015410 5ustar travistravissegyio-1.5.2/external/catch2/catch/0000775000372000037200000000000013253720451016472 5ustar travistravissegyio-1.5.2/external/catch2/catch/catch.hpp0000664000372000037200000145624313253720451020304 0ustar travistravis/* * Catch v2.0.1 * Generated: 2017-11-03 11:53:39.642003 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic ignored "-Wglobal-constructors" # pragma clang diagnostic ignored "-Wvariadic-macros" # pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif #endif // start catch_platform.h #ifdef __APPLE__ # include # if TARGET_OS_MAC == 1 # define CATCH_PLATFORM_MAC # elif TARGET_OS_IPHONE == 1 # define CATCH_PLATFORM_IPHONE # endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) # define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // start catch_tag_alias_autoregistrar.h // start catch_common.h // start catch_compiler_capabilities.h // Detect a number of compiler features - by compiler // The following features are defined: // // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. #ifdef __cplusplus # if __cplusplus >= 201402L # define CATCH_CPP14_OR_GREATER # endif #endif #ifdef __clang__ # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) # if !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # endif #endif #ifdef __OS400__ # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) # define CATCH_CONFIG_COLOUR_NONE # else # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Use of __COUNTER__ is suppressed during code analysis in // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif // end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #include #include #include namespace Catch { struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; protected: NonCopyable(); virtual ~NonCopyable(); }; struct SourceLineInfo { SourceLineInfo() = delete; SourceLineInfo( char const* _file, std::size_t _line ) noexcept; SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; bool empty() const noexcept; bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals bool isTrue( bool value ); bool alwaysTrue(); bool alwaysFalse(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO \ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) // end catch_common.h namespace Catch { struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h // start catch_interfaces_testcase.h #include #include namespace Catch { class TestSpec; struct ITestInvoker { virtual void invoke () const = 0; virtual ~ITestInvoker(); }; using ITestCasePtr = std::shared_ptr; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } // end catch_interfaces_testcase.h // start catch_stringref.h #include #include #include namespace Catch { class StringData; /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, /// it may not be null terminated. c_str() must return a null terminated /// string, however, and so the StringRef will internally take ownership /// (taking a copy), if necessary. In theory this ownership is not externally /// visible - but it does mean (substring) StringRefs should not be shared between /// threads. class StringRef { friend struct StringRefTestAccess; using size_type = std::size_t; char const* m_start; size_type m_size; char* m_data = nullptr; void takeOwnership(); public: // construction/ assignment StringRef() noexcept; StringRef( StringRef const& other ) noexcept; StringRef( StringRef&& other ) noexcept; StringRef( char const* rawChars ) noexcept; StringRef( char const* rawChars, size_type size ) noexcept; StringRef( std::string const& stdString ) noexcept; ~StringRef() noexcept; auto operator = ( StringRef other ) noexcept -> StringRef&; operator std::string() const; void swap( StringRef& other ) noexcept; public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator != ( StringRef const& other ) const noexcept -> bool; auto operator[] ( size_type index ) const noexcept -> char; public: // named queries auto empty() const noexcept -> bool; auto size() const noexcept -> size_type; auto numberOfCharacters() const noexcept -> size_type; auto c_str() const -> char const*; public: // substrings and searches auto substr( size_type start, size_type size ) const noexcept -> StringRef; private: // ownership queries - may not be consistent between calls auto isOwned() const noexcept -> bool; auto isSubstring() const noexcept -> bool; auto data() const noexcept -> char const*; }; auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; } // namespace Catch // end catch_stringref.h namespace Catch { template class TestInvokerAsMethod : public ITestInvoker { void (C::*m_testAsMethod)(); public: TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} void invoke() const override { C obj; (obj.*m_testAsMethod)(); } }; auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; template auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); } struct NameAndTags { NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; StringRef name; StringRef tags; }; struct AutoReg : NonCopyable { AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; ~AutoReg(); }; } // end namespace Catch #if defined(CATCH_CONFIG_DISABLE) #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ static void TestName() #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ namespace{ \ struct TestName : ClassName { \ void test(); \ }; \ } \ void TestName::test() #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS // end catch_test_registry.h // start catch_capture.hpp // start catch_assertionhandler.h // start catch_decomposer.h // start catch_tostring.h #include #include #include #include #include #ifdef __OBJC__ // start catch_objc_arc.hpp #import #ifdef __has_feature #define CATCH_ARC_ENABLED __has_feature(objc_arc) #else #define CATCH_ARC_ENABLED 0 #endif void arcSafeRelease( NSObject* obj ); id performOptionalSelector( id obj, SEL sel ); #if !CATCH_ARC_ENABLED inline void arcSafeRelease( NSObject* obj ) { [obj release]; } inline id performOptionalSelector( id obj, SEL sel ) { if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else inline void arcSafeRelease( NSObject* ){} inline id performOptionalSelector( id obj, SEL sel ) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; #ifdef __clang__ #pragma clang diagnostic pop #endif return nil; } #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained #define CATCH_ARC_STRONG __strong #endif // end catch_objc_arc.hpp #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif // We need a dummy global operator<< so we can bring it into Catch namespace later struct Catch_global_namespace_dummy; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { // Bring in operator<< from global namespace into Catch namespace using ::operator<<; namespace Detail { extern const std::string unprintableString; std::string rawMemoryToString( const void *object, std::size_t size ); template std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } template class IsStreamInsertable { template static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); template static auto test(...)->std::false_type; public: static const bool value = decltype(test(0))::value; }; } // namespace Detail // If we decide for C++14, change these to enable_if_ts template struct StringMaker { template static typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type convert(const Fake& t) { std::ostringstream sstr; sstr << t; return sstr.str(); } template static typename std::enable_if::value, std::string>::type convert(const Fake&) { return Detail::unprintableString; } }; namespace Detail { // This function dispatches all stringification requests inside of Catch. // Should be preferably called fully qualified, like ::Catch::Detail::stringify template std::string stringify(const T& e) { return ::Catch::StringMaker::type>::type>::convert(e); } } // namespace Detail // Some predefined specializations template<> struct StringMaker { static std::string convert(const std::string& str); }; template<> struct StringMaker { static std::string convert(const std::wstring& wstr); }; template<> struct StringMaker { static std::string convert(char const * str); }; template<> struct StringMaker { static std::string convert(char * str); }; template<> struct StringMaker { static std::string convert(wchar_t const * str); }; template<> struct StringMaker { static std::string convert(wchar_t * str); }; template struct StringMaker { static std::string convert(const char* str) { return ::Catch::Detail::stringify(std::string{ str }); } }; template struct StringMaker { static std::string convert(const char* str) { return ::Catch::Detail::stringify(std::string{ str }); } }; template struct StringMaker { static std::string convert(const char* str) { return ::Catch::Detail::stringify(std::string{ str }); } }; template<> struct StringMaker { static std::string convert(int value); }; template<> struct StringMaker { static std::string convert(long value); }; template<> struct StringMaker { static std::string convert(long long value); }; template<> struct StringMaker { static std::string convert(unsigned int value); }; template<> struct StringMaker { static std::string convert(unsigned long value); }; template<> struct StringMaker { static std::string convert(unsigned long long value); }; template<> struct StringMaker { static std::string convert(bool b); }; template<> struct StringMaker { static std::string convert(char c); }; template<> struct StringMaker { static std::string convert(signed char c); }; template<> struct StringMaker { static std::string convert(unsigned char c); }; template<> struct StringMaker { static std::string convert(std::nullptr_t); }; template<> struct StringMaker { static std::string convert(float value); }; template<> struct StringMaker { static std::string convert(double value); }; template struct StringMaker { template static std::string convert(U* p) { if (p) { return ::Catch::Detail::rawMemoryToString(p); } else { return "nullptr"; } } }; template struct StringMaker { static std::string convert(R C::* p) { if (p) { return ::Catch::Detail::rawMemoryToString(p); } else { return "nullptr"; } } }; namespace Detail { template std::string rangeToString(InputIterator first, InputIterator last) { std::ostringstream oss; oss << "{ "; if (first != last) { oss << ::Catch::Detail::stringify(*first); for (++first; first != last; ++first) oss << ", " << ::Catch::Detail::stringify(*first); } oss << " }"; return oss.str(); } } template struct StringMaker > { static std::string convert( std::vector const& v ) { return ::Catch::Detail::rangeToString( v.begin(), v.end() ); } }; template struct EnumStringMaker { static std::string convert(const T& t) { return ::Catch::Detail::stringify(static_cast::type>(t)); } }; #ifdef __OBJC__ template<> struct StringMaker { static std::string convert(NSString * nsstring) { if (!nsstring) return "nil"; return std::string("@") + [nsstring UTF8String]; } }; template<> struct StringMaker { static std::string convert(NSObject* nsObject) { return ::Catch::Detail::stringify([nsObject description]); } }; namespace Detail { inline std::string stringify( NSString* nsstring ) { return StringMaker::convert( nsstring ); } } // namespace Detail #endif // __OBJC__ } // namespace Catch ////////////////////////////////////////////////////// // Separate std-lib types stringification, so it can be selectively enabled // This means that we do not bring in #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) # define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER # define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER #endif // Separate std::pair specialization #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) #include namespace Catch { template struct StringMaker > { static std::string convert(const std::pair& pair) { std::ostringstream oss; oss << "{ " << ::Catch::Detail::stringify(pair.first) << ", " << ::Catch::Detail::stringify(pair.second) << " }"; return oss.str(); } }; } #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) #include namespace Catch { namespace Detail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct TupleElementPrinter { static void print(const Tuple& tuple, std::ostream& os) { os << (N ? ", " : " ") << ::Catch::Detail::stringify(std::get(tuple)); TupleElementPrinter::print(tuple, os); } }; template< typename Tuple, std::size_t N > struct TupleElementPrinter { static void print(const Tuple&, std::ostream&) {} }; } template struct StringMaker> { static std::string convert(const std::tuple& tuple) { std::ostringstream os; os << '{'; Detail::TupleElementPrinter>::print(tuple, os); os << " }"; return os.str(); } }; } #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER // Separate std::chrono::duration specialization #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) #include #include #include template struct ratio_string { static std::string symbol(); }; template std::string ratio_string::symbol() { std::ostringstream oss; oss << '[' << Ratio::num << '/' << Ratio::den << ']'; return oss.str(); } template <> struct ratio_string { static std::string symbol() { return "a"; } }; template <> struct ratio_string { static std::string symbol() { return "f"; } }; template <> struct ratio_string { static std::string symbol() { return "p"; } }; template <> struct ratio_string { static std::string symbol() { return "n"; } }; template <> struct ratio_string { static std::string symbol() { return "u"; } }; template <> struct ratio_string { static std::string symbol() { return "m"; } }; namespace Catch { //////////// // std::chrono::duration specializations template struct StringMaker> { static std::string convert(std::chrono::duration const& duration) { std::ostringstream oss; oss << duration.count() << ' ' << ratio_string::symbol() << 's'; return oss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { std::ostringstream oss; oss << duration.count() << " s"; return oss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { std::ostringstream oss; oss << duration.count() << " m"; return oss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { std::ostringstream oss; oss << duration.count() << " h"; return oss.str(); } }; //////////// // std::chrono::time_point specialization // Generic time_point cannot be specialized, only std::chrono::time_point template struct StringMaker> { static std::string convert(std::chrono::time_point const& time_point) { return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; } }; // std::chrono::time_point specialization template struct StringMaker> { static std::string convert(std::chrono::time_point const& time_point) { auto converted = std::chrono::system_clock::to_time_t(time_point); #ifdef _MSC_VER std::tm timeInfo = {}; gmtime_s(&timeInfo, &converted); #else std::tm* timeInfo = std::gmtime(&converted); #endif auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); char timeStamp[timeStampSize]; const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif return std::string(timeStamp); } }; } #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER #ifdef _MSC_VER #pragma warning(pop) #endif // end catch_tostring.h #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #pragma warning(disable:4018) // more "signed/unsigned mismatch" #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #pragma warning(disable:4180) // qualifier applied to function type has no meaning #endif namespace Catch { struct ITransientExpression { virtual auto isBinaryExpression() const -> bool = 0; virtual auto getResult() const -> bool = 0; virtual void streamReconstructedExpression( std::ostream &os ) const = 0; // We don't actually need a virtual destructore, but many static analysers // complain if it's not here :-( virtual ~ITransientExpression(); }; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template class BinaryExpr : public ITransientExpression { bool m_result; LhsT m_lhs; StringRef m_op; RhsT m_rhs; auto isBinaryExpression() const -> bool override { return true; } auto getResult() const -> bool override { return m_result; } void streamReconstructedExpression( std::ostream &os ) const override { formatReconstructedExpression ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); } public: BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) : m_result( comparisonResult ), m_lhs( lhs ), m_op( op ), m_rhs( rhs ) {} }; template class UnaryExpr : public ITransientExpression { LhsT m_lhs; auto isBinaryExpression() const -> bool override { return false; } auto getResult() const -> bool override { return m_lhs ? true : false; } void streamReconstructedExpression( std::ostream &os ) const override { os << Catch::Detail::stringify( m_lhs ); } public: UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} }; // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) template auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; template auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } template auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } template auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } template auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } template auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; template auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } template auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } template auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } template auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } template class ExprLhs { LhsT m_lhs; public: ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} template auto operator == ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); } auto operator == ( bool rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs == rhs, m_lhs, "==", rhs ); } template auto operator != ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); } auto operator != ( bool rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs != rhs, m_lhs, "!=", rhs ); } template auto operator > ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs > rhs, m_lhs, ">", rhs ); } template auto operator < ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs < rhs, m_lhs, "<", rhs ); } template auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs >= rhs, m_lhs, ">=", rhs ); } template auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { return BinaryExpr( m_lhs <= rhs, m_lhs, "<=", rhs ); } auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr( m_lhs ); } }; void handleExpression( ITransientExpression const& expr ); template void handleExpression( ExprLhs const& expr ) { handleExpression( expr.makeUnaryExpr() ); } struct Decomposer { template auto operator <= ( T const& lhs ) -> ExprLhs { return ExprLhs( lhs ); } auto operator <=( bool value ) -> ExprLhs { return ExprLhs( value ); } }; } // end namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif // end catch_decomposer.h // start catch_assertioninfo.h // start catch_result_type.h namespace Catch { // ResultWas::OfType enum struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, Warning = 2, FailureBit = 0x10, ExpressionFailed = FailureBit | 1, ExplicitFailure = FailureBit | 2, Exception = 0x100 | FailureBit, ThrewException = Exception | 1, DidntThrowException = Exception | 2, FatalErrorCondition = 0x200 | FailureBit }; }; bool isOk( ResultWas::OfType resultType ); bool isJustInfo( int flags ); // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { Normal = 0x01, ContinueOnFailure = 0x02, // Failures fail test, but execution continues FalseTest = 0x04, // Prefix expression with ! SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); bool shouldContinueOnFailure( int flags ); bool isFalseTest( int flags ); bool shouldSuppressFailure( int flags ); } // end namespace Catch // end catch_result_type.h namespace Catch { struct AssertionInfo { StringRef macroName; SourceLineInfo lineInfo; StringRef capturedExpression; ResultDisposition::Flags resultDisposition; // We want to delete this constructor but a compiler bug in 4.8 means // the struct is then treated as non-aggregate //AssertionInfo() = delete; }; } // end namespace Catch // end catch_assertioninfo.h namespace Catch { struct TestFailureException{}; struct AssertionResultData; class LazyExpression { friend class AssertionHandler; friend struct AssertionStats; ITransientExpression const* m_transientExpression = nullptr; bool m_isNegated; public: LazyExpression( bool isNegated ); LazyExpression( LazyExpression const& other ); LazyExpression& operator = ( LazyExpression const& ) = delete; explicit operator bool() const; friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; }; class AssertionHandler { AssertionInfo m_assertionInfo; bool m_shouldDebugBreak = false; bool m_shouldThrow = false; bool m_inExceptionGuard = false; public: AssertionHandler ( StringRef macroName, SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ); ~AssertionHandler(); void handle( ITransientExpression const& expr ); template void handle( ExprLhs const& expr ) { handle( expr.makeUnaryExpr() ); } void handle( ResultWas::OfType resultType ); void handle( ResultWas::OfType resultType, StringRef const& message ); void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ); void handle( AssertionResultData const& resultData, ITransientExpression const* expr ); auto shouldDebugBreak() const -> bool; auto allowThrows() const -> bool; void reactWithDebugBreak() const; void reactWithoutDebugBreak() const; void useActiveException(); void setExceptionGuard(); void unsetExceptionGuard(); }; void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); } // namespace Catch // end catch_assertionhandler.h // start catch_message.h #include #include namespace Catch { struct MessageInfo { MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); std::string macroName; std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; unsigned int sequence; bool operator == ( MessageInfo const& other ) const; bool operator < ( MessageInfo const& other ) const; private: static unsigned int globalCount; }; struct MessageStream { template MessageStream& operator << ( T const& value ) { m_stream << value; return *this; } // !TBD reuse a global/ thread-local stream std::ostringstream m_stream; }; struct MessageBuilder : MessageStream { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ); template MessageBuilder& operator << ( T const& value ) { m_stream << value; return *this; } MessageInfo m_info; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); ~ScopedMessage(); MessageInfo m_info; }; } // end namespace Catch // end catch_message.h // start catch_interfaces_capture.h #include namespace Catch { class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; struct Counts; struct BenchmarkInfo; struct BenchmarkStats; struct IResultCapture { virtual ~IResultCapture(); virtual void assertionStarting( AssertionInfo const& info ) = 0; virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; virtual void exceptionEarlyReported() = 0; virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual bool lastAssertionPassed() = 0; virtual void assertionPassed() = 0; virtual void assertionRun() = 0; }; IResultCapture& getResultCapture(); } // end catch_interfaces_capture.h // start catch_debugger.h namespace Catch { bool isDebuggerActive(); } #ifdef CATCH_PLATFORM_MAC #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ #elif defined(CATCH_PLATFORM_LINUX) // If we can use inline assembler, do it because this allows us to break // directly at the location of the failing check instead of breaking inside // raise() called from it, i.e. one stack frame below. #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ #else // Fall back to the generic way. #include #define CATCH_TRAP() raise(SIGTRAP) #endif #elif defined(_MSC_VER) #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define CATCH_TRAP() DebugBreak() #endif #ifdef CATCH_TRAP #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } #else #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // end catch_debugger.h #if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif #if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// // We can speedup compilation significantly by breaking into debugger lower in // the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER // macro in each assertion #define INTERNAL_CATCH_REACT( handler ) \ handler.reactWithDebugBreak(); /////////////////////////////////////////////////////////////////////////////// // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. // This can potentially cause false negative, if the test code catches // the exception before it propagates back up to the runner. #define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); #define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); #else // CATCH_CONFIG_FAST_COMPILE /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. // This needs to be done as a macro so the debugger will stop in the user // source code rather than in Catch library code #define INTERNAL_CATCH_REACT( handler ) \ if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ handler.reactWithoutDebugBreak(); #define INTERNAL_CATCH_TRY( capturer ) try #define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::isTrue( false && static_cast( !!(__VA_ARGS__) ) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ static_cast(__VA_ARGS__); \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ catchAssertionHandler.useActiveException(); \ } \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ else \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(expr); \ catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType const& ) { \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ catchAssertionHandler.useActiveException(); \ } \ else \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ } \ else \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) #endif // CATCH_CONFIG_DISABLE // end catch_capture.hpp // start catch_section.h // start catch_section_info.h // start catch_totals.h #include namespace Catch { struct Counts { Counts operator - ( Counts const& other ) const; Counts& operator += ( Counts const& other ); std::size_t total() const; bool allPassed() const; bool allOk() const; std::size_t passed = 0; std::size_t failed = 0; std::size_t failedButOk = 0; }; struct Totals { Totals operator - ( Totals const& other ) const; Totals& operator += ( Totals const& other ); Totals delta( Totals const& prevTotals ) const; Counts assertions; Counts testCases; }; } // end catch_totals.h #include namespace Catch { struct SectionInfo { SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description = std::string() ); std::string name; std::string description; SourceLineInfo lineInfo; }; struct SectionEndInfo { SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; }; } // end namespace Catch // end catch_section_info.h // start catch_timer.h #include namespace Catch { auto getCurrentNanosecondsSinceEpoch() -> uint64_t; auto getEstimatedClockResolution() -> uint64_t; class Timer { uint64_t m_nanoseconds = 0; public: void start(); auto getElapsedNanoseconds() const -> unsigned int; auto getElapsedMicroseconds() const -> unsigned int; auto getElapsedMilliseconds() const -> unsigned int; auto getElapsedSeconds() const -> double; }; } // namespace Catch // end catch_timer.h #include namespace Catch { class Section : NonCopyable { public: Section( SectionInfo const& info ); ~Section(); // This indicates whether the section should be executed or not explicit operator bool() const; private: SectionInfo m_info; std::string m_name; Counts m_assertions; bool m_sectionIncluded; Timer m_timer; }; } // end namespace Catch #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) // end catch_section.h // start catch_benchmark.h #include #include namespace Catch { class BenchmarkLooper { std::string m_name; std::size_t m_count = 0; std::size_t m_iterationsToRun = 1; uint64_t m_resolution; Timer m_timer; static auto getResolution() -> uint64_t; public: // Keep most of this inline as it's on the code path that is being timed BenchmarkLooper( StringRef name ) : m_name( name ), m_resolution( getResolution() ) { reportStart(); m_timer.start(); } explicit operator bool() { if( m_count < m_iterationsToRun ) return true; return needsMoreIterations(); } void increment() { ++m_count; } void reportStart(); auto needsMoreIterations() -> bool; }; } // end namespace Catch #define BENCHMARK( name ) \ for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) // end catch_benchmark.h // start catch_interfaces_exception.h // start catch_interfaces_registry_hub.h #include #include namespace Catch { class TestCase; struct ITestCaseRegistry; struct IExceptionTranslatorRegistry; struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; struct ITagAliasRegistry; class StartupExceptionRegistry; using IReporterFactoryPtr = std::shared_ptr; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; virtual void registerStartupException() noexcept = 0; }; IRegistryHub& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } // end catch_interfaces_registry_hub.h #if defined(CATCH_CONFIG_DISABLE) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ static std::string translatorName( signature ) #endif #include #include #include namespace Catch { using exceptionTranslateFunction = std::string(*)(); struct IExceptionTranslator; using ExceptionTranslators = std::vector>; struct IExceptionTranslator { virtual ~IExceptionTranslator(); virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; struct IExceptionTranslatorRegistry { virtual ~IExceptionTranslatorRegistry(); virtual std::string translateActiveException() const = 0; }; class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: ExceptionTranslator( std::string(*translateFunction)( T& ) ) : m_translateFunction( translateFunction ) {} std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { try { if( it == itEnd ) std::rethrow_exception(std::current_exception()); else return (*it)->translate( it+1, itEnd ); } catch( T& ex ) { return m_translateFunction( ex ); } } protected: std::string(*m_translateFunction)( T& ); }; public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { getMutableRegistryHub().registerTranslator ( new ExceptionTranslator( translateFunction ) ); } }; } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // end catch_interfaces_exception.h // start catch_approx.h // start catch_enforce.h #include #include #define CATCH_PREPARE_EXCEPTION( type, msg ) \ type( static_cast( std::ostringstream() << msg ).str() ) #define CATCH_INTERNAL_ERROR( msg ) \ throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); #define CATCH_ERROR( msg ) \ throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) #define CATCH_ENFORCE( condition, msg ) \ do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) // end catch_enforce.h #include namespace Catch { namespace Detail { class Approx { private: bool equalityComparisonImpl(double other) const; public: explicit Approx ( double value ); static Approx custom(); template ::value>::type> Approx operator()( T const& value ) { Approx approx( static_cast(value) ); approx.epsilon( m_epsilon ); approx.margin( m_margin ); approx.scale( m_scale ); return approx; } template ::value>::type> explicit Approx( T const& value ): Approx(static_cast(value)) {} template ::value>::type> friend bool operator == ( const T& lhs, Approx const& rhs ) { auto lhs_v = static_cast(lhs); return rhs.equalityComparisonImpl(lhs_v); } template ::value>::type> friend bool operator == ( Approx const& lhs, const T& rhs ) { return operator==( rhs, lhs ); } template ::value>::type> friend bool operator != ( T const& lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } template ::value>::type> friend bool operator != ( Approx const& lhs, T const& rhs ) { return !operator==( rhs, lhs ); } template ::value>::type> friend bool operator <= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) < rhs.m_value || lhs == rhs; } template ::value>::type> friend bool operator <= ( Approx const& lhs, T const& rhs ) { return lhs.m_value < static_cast(rhs) || lhs == rhs; } template ::value>::type> friend bool operator >= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) > rhs.m_value || lhs == rhs; } template ::value>::type> friend bool operator >= ( Approx const& lhs, T const& rhs ) { return lhs.m_value > static_cast(rhs) || lhs == rhs; } template ::value>::type> Approx& epsilon( T const& newEpsilon ) { double epsilonAsDouble = static_cast(newEpsilon); CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, "Invalid Approx::epsilon: " << epsilonAsDouble << ", Approx::epsilon has to be between 0 and 1"); m_epsilon = epsilonAsDouble; return *this; } template ::value>::type> Approx& margin( T const& newMargin ) { double marginAsDouble = static_cast(newMargin); CATCH_ENFORCE(marginAsDouble >= 0, "Invalid Approx::margin: " << marginAsDouble << ", Approx::Margin has to be non-negative."); m_margin = marginAsDouble; return *this; } template ::value>::type> Approx& scale( T const& newScale ) { m_scale = static_cast(newScale); return *this; } std::string toString() const; private: double m_epsilon; double m_margin; double m_scale; double m_value; }; } template<> struct StringMaker { static std::string convert(Catch::Detail::Approx const& value); }; } // end namespace Catch // end catch_approx.h // start catch_string_manip.h #include #include namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ); bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); std::string trim( std::string const& str ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); struct pluralise { pluralise( std::size_t count, std::string const& label ); friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); std::size_t m_count; std::string m_label; }; } // end catch_string_manip.h #ifndef CATCH_CONFIG_DISABLE_MATCHERS // start catch_capture_matchers.h // start catch_matchers.h #include #include namespace Catch { namespace Matchers { namespace Impl { template struct MatchAllOf; template struct MatchAnyOf; template struct MatchNotOf; class MatcherUntypedBase { public: MatcherUntypedBase() = default; MatcherUntypedBase ( MatcherUntypedBase const& ) = default; MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; std::string toString() const; protected: virtual ~MatcherUntypedBase(); virtual std::string describe() const = 0; mutable std::string m_cachedToString; }; template struct MatcherMethod { virtual bool match( ObjectT const& arg ) const = 0; }; template struct MatcherMethod { virtual bool match( PtrT* arg ) const = 0; }; template struct MatcherBase : MatcherUntypedBase, MatcherMethod { MatchAllOf operator && ( MatcherBase const& other ) const; MatchAnyOf operator || ( MatcherBase const& other ) const; MatchNotOf operator ! () const; }; template struct MatchAllOf : MatcherBase { bool match( ArgT const& arg ) const override { for( auto matcher : m_matchers ) { if (!matcher->match(arg)) return false; } return true; } std::string describe() const override { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; bool first = true; for( auto matcher : m_matchers ) { if( first ) first = false; else description += " and "; description += matcher->toString(); } description += " )"; return description; } MatchAllOf& operator && ( MatcherBase const& other ) { m_matchers.push_back( &other ); return *this; } std::vector const*> m_matchers; }; template struct MatchAnyOf : MatcherBase { bool match( ArgT const& arg ) const override { for( auto matcher : m_matchers ) { if (matcher->match(arg)) return true; } return false; } std::string describe() const override { std::string description; description.reserve( 4 + m_matchers.size()*32 ); description += "( "; bool first = true; for( auto matcher : m_matchers ) { if( first ) first = false; else description += " or "; description += matcher->toString(); } description += " )"; return description; } MatchAnyOf& operator || ( MatcherBase const& other ) { m_matchers.push_back( &other ); return *this; } std::vector const*> m_matchers; }; template struct MatchNotOf : MatcherBase { MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} bool match( ArgT const& arg ) const override { return !m_underlyingMatcher.match( arg ); } std::string describe() const override { return "not " + m_underlyingMatcher.toString(); } MatcherBase const& m_underlyingMatcher; }; template MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { return MatchAllOf() && *this && other; } template MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { return MatchAnyOf() || *this || other; } template MatchNotOf MatcherBase::operator ! () const { return MatchNotOf( *this ); } } // namespace Impl } // namespace Matchers using namespace Matchers; using Matchers::Impl::MatcherBase; } // namespace Catch // end catch_matchers.h // start catch_matchers_string.h #include namespace Catch { namespace Matchers { namespace StdString { struct CasedString { CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); std::string adjustString( std::string const& str ) const; std::string caseSensitivitySuffix() const; CaseSensitive::Choice m_caseSensitivity; std::string m_str; }; struct StringMatcherBase : MatcherBase { StringMatcherBase( std::string const& operation, CasedString const& comparator ); std::string describe() const override; CasedString m_comparator; std::string m_operation; }; struct EqualsMatcher : StringMatcherBase { EqualsMatcher( CasedString const& comparator ); bool match( std::string const& source ) const override; }; struct ContainsMatcher : StringMatcherBase { ContainsMatcher( CasedString const& comparator ); bool match( std::string const& source ) const override; }; struct StartsWithMatcher : StringMatcherBase { StartsWithMatcher( CasedString const& comparator ); bool match( std::string const& source ) const override; }; struct EndsWithMatcher : StringMatcherBase { EndsWithMatcher( CasedString const& comparator ); bool match( std::string const& source ) const override; }; } // namespace StdString // The following functions create the actual matcher objects. // This allows the types to be inferred StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); } // namespace Matchers } // namespace Catch // end catch_matchers_string.h // start catch_matchers_vector.h namespace Catch { namespace Matchers { namespace Vector { template struct ContainsElementMatcher : MatcherBase, T> { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} bool match(std::vector const &v) const override { for (auto const& el : v) { if (el == m_comparator) { return true; } } return false; } std::string describe() const override { return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } T const& m_comparator; }; template struct ContainsMatcher : MatcherBase, std::vector > { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} bool match(std::vector const &v) const override { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; for (auto const& comparator : m_comparator) { auto present = false; for (const auto& el : v) { if (el == comparator) { present = true; break; } } if (!present) { return false; } } return true; } std::string describe() const override { return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; }; template struct EqualsMatcher : MatcherBase, std::vector > { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} bool match(std::vector const &v) const override { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; for (std::size_t i = 0; i < v.size(); ++i) if (m_comparator[i] != v[i]) return false; return true; } std::string describe() const override { return "Equals: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; }; } // namespace Vector // The following functions create the actual matcher objects. // This allows the types to be inferred template Vector::ContainsMatcher Contains( std::vector const& comparator ) { return Vector::ContainsMatcher( comparator ); } template Vector::ContainsElementMatcher VectorContains( T const& comparator ) { return Vector::ContainsElementMatcher( comparator ); } template Vector::EqualsMatcher Equals( std::vector const& comparator ) { return Vector::EqualsMatcher( comparator ); } } // namespace Matchers } // namespace Catch // end catch_matchers_vector.h namespace Catch { template class MatchExpr : public ITransientExpression { ArgT const& m_arg; MatcherT m_matcher; StringRef m_matcherString; bool m_result; public: MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ), m_result( matcher.match( arg ) ) {} auto isBinaryExpression() const -> bool override { return true; } auto getResult() const -> bool override { return m_result; } void streamReconstructedExpression( std::ostream &os ) const override { auto matcherAsString = m_matcher.toString(); os << Catch::Detail::stringify( m_arg ) << ' '; if( matcherAsString == Detail::unprintableString ) os << m_matcherString; else os << matcherAsString; } }; using StringMatcher = Matchers::Impl::MatcherBase; void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); template auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { return MatchExpr( arg, matcher, matcherString ); } } // namespace Catch /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__ ); \ catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType const& ex ) { \ catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ } \ catch( ... ) { \ catchAssertionHandler.useActiveException(); \ } \ else \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( Catch::alwaysFalse() ) // end catch_capture_matchers.h #endif // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections // start catch_test_case_info.h #include #include #include #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { struct ITestInvoker; struct TestCaseInfo { enum SpecialProperties{ None = 0, IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4, NonPortable = 1 << 5, Benchmark = 1 << 6 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::vector const& _tags, SourceLineInfo const& _lineInfo ); friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; std::string tagsAsString() const; std::string name; std::string className; std::string description; std::vector tags; std::vector lcaseTags; SourceLineInfo lineInfo; SpecialProperties properties; }; class TestCase : public TestCaseInfo { public: TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); TestCase withName( std::string const& _newName ) const; void invoke() const; TestCaseInfo const& getTestCaseInfo() const; bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; private: std::shared_ptr test; }; TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); } #ifdef __clang__ #pragma clang diagnostic pop #endif // end catch_test_case_info.h // start catch_interfaces_runner.h namespace Catch { struct IRunner { virtual ~IRunner(); virtual bool aborting() const = 0; }; } // end catch_interfaces_runner.h #ifdef __OBJC__ // start catch_objc.hpp #import #include // NB. Any general catch headers included here must be included // in catch.hpp first to make sure they are included by the single // header for non obj-usage /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @protocol OcFixture @optional -(void) setUp; -(void) tearDown; @end namespace Catch { class OcMethod : public ITestInvoker { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} virtual void invoke() const { id obj = [[m_cls alloc] init]; performOptionalSelector( obj, @selector(setUp) ); performOptionalSelector( obj, m_sel ); performOptionalSelector( obj, @selector(tearDown) ); arcSafeRelease( obj ); } private: virtual ~OcMethod() {} Class m_cls; SEL m_sel; }; namespace Detail{ inline std::string getAnnotation( Class cls, std::string const& annotationName, std::string const& testCaseName ) { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); arcSafeRelease( selStr ); id value = performOptionalSelector( cls, sel ); if( value ) return [(NSString*)value UTF8String]; return ""; } } inline std::size_t registerTestMethods() { std::size_t noTestMethods = 0; int noClasses = objc_getClassList( nullptr, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { Class cls = classes[c]; { u_int count; Method* methods = class_copyMethodList( cls, &count ); for( u_int m = 0; m < count ; m++ ) { SEL selector = method_getName(methods[m]); std::string methodName = sel_getName(selector); if( startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); noTestMethods++; } } free(methods); } } return noTestMethods; } #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) namespace Matchers { namespace Impl { namespace NSStringMatchers { struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } bool match( NSString* arg ) const override { return false; } NSString* CATCH_ARC_STRONG m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } std::string describe() const override { return "equals string: " + Catch::Detail::stringify( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } std::string describe() const override { return "contains string: " + Catch::Detail::stringify( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } std::string describe() const override { return "starts with: " + Catch::Detail::stringify( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } std::string describe() const override { return "ends with: " + Catch::Detail::stringify( m_substr ); } }; } // namespace NSStringMatchers } // namespace Impl inline Impl::NSStringMatchers::Equals Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } inline Impl::NSStringMatchers::Contains Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } inline Impl::NSStringMatchers::StartsWith StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } inline Impl::NSStringMatchers::EndsWith EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } } // namespace Matchers using namespace Matchers; #endif // CATCH_CONFIG_DISABLE_MATCHERS } // namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix #define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ { \ return @ name; \ } \ +(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ { \ return @ desc; \ } \ -(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) #define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) // end catch_objc.hpp #endif #ifdef CATCH_CONFIG_EXTERNAL_INTERFACES // start catch_external_interfaces.h // start catch_reporter_bases.hpp // start catch_interfaces_reporter.h // start catch_config.hpp // start catch_test_spec_parser.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // start catch_test_spec.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // start catch_wildcard_pattern.h namespace Catch { class WildcardPattern { enum WildcardPosition { NoWildcard = 0, WildcardAtStart = 1, WildcardAtEnd = 2, WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; public: WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); virtual ~WildcardPattern() = default; virtual bool matches( std::string const& str ) const; private: std::string adjustCase( std::string const& str ) const; CaseSensitive::Choice m_caseSensitivity; WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; }; } // end catch_wildcard_pattern.h #include #include #include namespace Catch { class TestSpec { struct Pattern { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; using PatternPtr = std::shared_ptr; class NamePattern : public Pattern { public: NamePattern( std::string const& name ); virtual ~NamePattern(); virtual bool matches( TestCaseInfo const& testCase ) const override; private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: TagPattern( std::string const& tag ); virtual ~TagPattern(); virtual bool matches( TestCaseInfo const& testCase ) const override; private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: ExcludedPattern( PatternPtr const& underlyingPattern ); virtual ~ExcludedPattern(); virtual bool matches( TestCaseInfo const& testCase ) const override; private: PatternPtr m_underlyingPattern; }; struct Filter { std::vector m_patterns; bool matches( TestCaseInfo const& testCase ) const; }; public: bool hasFilters() const; bool matches( TestCaseInfo const& testCase ) const; private: std::vector m_filters; friend class TestSpecParser; }; } #ifdef __clang__ #pragma clang diagnostic pop #endif // end catch_test_spec.h // start catch_interfaces_tag_alias_registry.h #include namespace Catch { struct TagAlias; struct ITagAliasRegistry { virtual ~ITagAliasRegistry(); // Nullptr if not present virtual TagAlias const* find( std::string const& alias ) const = 0; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; static ITagAliasRegistry const& get(); }; } // end namespace Catch // end catch_interfaces_tag_alias_registry.h namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag, EscapedName }; Mode m_mode = None; bool m_exclusion = false; std::size_t m_start = std::string::npos, m_pos = 0; std::string m_arg; std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases = nullptr; public: TestSpecParser( ITagAliasRegistry const& tagAliases ); TestSpecParser& parse( std::string const& arg ); TestSpec testSpec(); private: void visitChar( char c ); void startNewMode( Mode mode, std::size_t start ); void escape(); std::string subString() const; template void addPattern() { std::string token = subString(); for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { TestSpec::PatternPtr pattern = std::make_shared( token ); if( m_exclusion ) pattern = std::make_shared( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } void addFilter(); }; TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif // end catch_test_spec_parser.h // start catch_interfaces_config.h #include #include #include #include namespace Catch { enum class Verbosity { Quiet = 0, Normal, High }; struct WarnAbout { enum What { Nothing = 0x00, NoAssertions = 0x01 }; }; struct ShowDurations { enum OrNot { DefaultForReporter, Always, Never }; }; struct RunTests { enum InWhatOrder { InDeclarationOrder, InLexicographicalOrder, InRandomOrder }; }; struct UseColour { enum YesOrNo { Auto, Yes, No }; }; struct WaitForKeypress { enum When { Never, BeforeStart = 1, BeforeExit = 2, BeforeStartAndExit = BeforeStart | BeforeExit }; }; class TestSpec; struct IConfig : NonCopyable { virtual ~IConfig(); virtual bool allowThrows() const = 0; virtual std::ostream& stream() const = 0; virtual std::string name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual int benchmarkResolutionMultiple() const = 0; virtual UseColour::YesOrNo useColour() const = 0; virtual std::vector const& getSectionsToRun() const = 0; virtual Verbosity verbosity() const = 0; }; using IConfigPtr = std::shared_ptr; } // end catch_interfaces_config.h // Libstdc++ doesn't like incomplete classes for unique_ptr // start catch_stream.h // start catch_streambuf.h #include namespace Catch { class StreamBufBase : public std::streambuf { public: virtual ~StreamBufBase(); }; } // end catch_streambuf.h #include #include #include #include namespace Catch { std::ostream& cout(); std::ostream& cerr(); std::ostream& clog(); struct IStream { virtual ~IStream(); virtual std::ostream& stream() const = 0; }; class FileStream : public IStream { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); ~FileStream() override = default; public: // IStream std::ostream& stream() const override; }; class CoutStream : public IStream { mutable std::ostream m_os; public: CoutStream(); ~CoutStream() override = default; public: // IStream std::ostream& stream() const override; }; class DebugOutStream : public IStream { std::unique_ptr m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); ~DebugOutStream() override = default; public: // IStream std::ostream& stream() const override; }; } // end catch_stream.h #include #include #include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { struct IStream; struct ConfigData { bool listTests = false; bool listTags = false; bool listReporters = false; bool listTestNamesOnly = false; bool showSuccessfulTests = false; bool shouldDebugBreak = false; bool noThrow = false; bool showHelp = false; bool showInvisibles = false; bool filenamesAsTags = false; bool libIdentify = false; int abortAfter = -1; unsigned int rngSeed = 0; int benchmarkResolutionMultiple = 100; Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; UseColour::YesOrNo useColour = UseColour::Auto; WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; std::string outputFilename; std::string name; std::string processName; std::vector reporterNames; std::vector testsOrTags; std::vector sectionsToRun; }; class Config : public IConfig { public: Config() = default; Config( ConfigData const& data ); virtual ~Config() = default; std::string const& getFilename() const; bool listTests() const; bool listTestNamesOnly() const; bool listTags() const; bool listReporters() const; std::string getProcessName() const; std::vector const& getReporterNames() const; std::vector const& getSectionsToRun() const override; virtual TestSpec const& testSpec() const override; bool showHelp() const; // IConfig interface bool allowThrows() const override; std::ostream& stream() const override; std::string name() const override; bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; ShowDurations::OrNot showDurations() const override; RunTests::InWhatOrder runOrder() const override; unsigned int rngSeed() const override; int benchmarkResolutionMultiple() const override; UseColour::YesOrNo useColour() const override; bool shouldDebugBreak() const override; int abortAfter() const override; bool showInvisibles() const override; Verbosity verbosity() const override; private: IStream const* openStream(); ConfigData m_data; std::unique_ptr m_stream; TestSpec m_testSpec; }; } // end namespace Catch // end catch_config.hpp // start catch_assertionresult.h #include namespace Catch { struct AssertionResultData { AssertionResultData() = delete; AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); std::string message; mutable std::string reconstructedExpression; LazyExpression lazyExpression; ResultWas::OfType resultType; std::string reconstructExpression() const; }; class AssertionResult { public: AssertionResult() = delete; AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); bool isOk() const; bool succeeded() const; ResultWas::OfType getResultType() const; bool hasExpression() const; bool hasMessage() const; std::string getExpression() const; std::string getExpressionInMacro() const; bool hasExpandedExpression() const; std::string getExpandedExpression() const; std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; //protected: AssertionInfo m_info; AssertionResultData m_resultData; }; } // end namespace Catch // end catch_assertionresult.h // start catch_option.hpp namespace Catch { // An optional type template class Option { public: Option() : nullableValue( nullptr ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) {} ~Option() { reset(); } Option& operator= ( Option const& _other ) { if( &_other != this ) { reset(); if( _other ) nullableValue = new( storage ) T( *_other ); } return *this; } Option& operator = ( T const& _value ) { reset(); nullableValue = new( storage ) T( _value ); return *this; } void reset() { if( nullableValue ) nullableValue->~T(); nullableValue = nullptr; } T& operator*() { return *nullableValue; } T const& operator*() const { return *nullableValue; } T* operator->() { return nullableValue; } const T* operator->() const { return nullableValue; } T valueOr( T const& defaultValue ) const { return nullableValue ? *nullableValue : defaultValue; } bool some() const { return nullableValue != nullptr; } bool none() const { return nullableValue == nullptr; } bool operator !() const { return nullableValue == nullptr; } explicit operator bool() const { return some(); } private: T *nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; } // end namespace Catch // end catch_option.hpp #include #include #include #include #include namespace Catch { struct ReporterConfig { explicit ReporterConfig( IConfigPtr const& _fullConfig ); ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); std::ostream& stream() const; IConfigPtr fullConfig() const; private: std::ostream* m_stream; IConfigPtr m_fullConfig; }; struct ReporterPreferences { bool shouldRedirectStdOut = false; }; template struct LazyStat : Option { LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; return *this; } void reset() { Option::reset(); used = false; } bool used = false; }; struct TestRunInfo { TestRunInfo( std::string const& _name ); std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ); std::string name; std::size_t groupIndex; std::size_t groupsCounts; }; struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, Totals const& _totals ); AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; virtual ~AssertionStats(); AssertionResult assertionResult; std::vector infoMessages; Totals totals; }; struct SectionStats { SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, bool _missingAssertions ); SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; virtual ~SectionStats(); SectionInfo sectionInfo; Counts assertions; double durationInSeconds; bool missingAssertions; }; struct TestCaseStats { TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, bool _aborting ); TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; virtual ~TestCaseStats(); TestCaseInfo testInfo; Totals totals; std::string stdOut; std::string stdErr; bool aborting; }; struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, bool _aborting ); TestGroupStats( GroupInfo const& _groupInfo ); TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; virtual ~TestGroupStats(); GroupInfo groupInfo; Totals totals; bool aborting; }; struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, bool _aborting ); TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; virtual ~TestRunStats(); TestRunInfo runInfo; Totals totals; bool aborting; }; struct BenchmarkInfo { std::string name; }; struct BenchmarkStats { BenchmarkInfo info; std::size_t iterations; uint64_t elapsedTimeInNanoseconds; }; struct IStreamingReporter { virtual ~IStreamingReporter() = default; // Implementing class must also provide the following static methods: // static std::string getDescription(); // static std::set getSupportedVerbosities() virtual ReporterPreferences getPreferences() const = 0; virtual void noMatchingTestCases( std::string const& spec ) = 0; virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; // *** experimental *** virtual void benchmarkStarting( BenchmarkInfo const& ) {} virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; // *** experimental *** virtual void benchmarkEnded( BenchmarkStats const& ) {} virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; // Default empty implementation provided virtual void fatalErrorEncountered( StringRef name ); virtual bool isMulti() const; }; using IStreamingReporterPtr = std::unique_ptr; struct IReporterFactory { virtual ~IReporterFactory(); virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; using IReporterFactoryPtr = std::shared_ptr; struct IReporterRegistry { using FactoryMap = std::map; using Listeners = std::vector; virtual ~IReporterRegistry(); virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); } // end namespace Catch // end catch_interfaces_reporter.h #include #include #include #include #include #include namespace Catch { void prepareExpandedExpression(AssertionResult& result); // Returns double formatted as %.3f (format expected on output) std::string getFormattedDuration( double duration ); template struct StreamingReporterBase : IStreamingReporter { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } ReporterPreferences getPreferences() const override { return m_reporterPrefs; } static std::set getSupportedVerbosities() { return { Verbosity::Normal }; } ~StreamingReporterBase() override = default; void noMatchingTestCases(std::string const&) override {} void testRunStarting(TestRunInfo const& _testRunInfo) override { currentTestRunInfo = _testRunInfo; } void testGroupStarting(GroupInfo const& _groupInfo) override { currentGroupInfo = _groupInfo; } void testCaseStarting(TestCaseInfo const& _testInfo) override { currentTestCaseInfo = _testInfo; } void sectionStarting(SectionInfo const& _sectionInfo) override { m_sectionStack.push_back(_sectionInfo); } void sectionEnded(SectionStats const& /* _sectionStats */) override { m_sectionStack.pop_back(); } void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { currentTestCaseInfo.reset(); } void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { currentGroupInfo.reset(); } void testRunEnded(TestRunStats const& /* _testRunStats */) override { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } void skipTest(TestCaseInfo const&) override { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } IConfigPtr m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; ReporterPreferences m_reporterPrefs; }; template struct CumulativeReporterBase : IStreamingReporter { template struct Node { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} using ChildNodes = std::vector>; T value; ChildNodes children; }; struct SectionNode { explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} virtual ~SectionNode() = default; bool operator == (SectionNode const& other) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } bool operator == (std::shared_ptr const& other) const { return operator==(*other); } SectionStats stats; using ChildSections = std::vector>; using Assertions = std::vector; ChildSections childSections; Assertions assertions; std::string stdOut; std::string stdErr; }; struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() (std::shared_ptr const& node) const { return ((node->stats.sectionInfo.name == m_other.name) && (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } void operator=(BySectionInfo const&) = delete; private: SectionInfo const& m_other; }; using TestCaseNode = Node; using TestGroupNode = Node; using TestRunNode = Node; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); } ~CumulativeReporterBase() override = default; ReporterPreferences getPreferences() const override { return m_reporterPrefs; } static std::set getSupportedVerbosities() { return { Verbosity::Normal }; } void testRunStarting( TestRunInfo const& ) override {} void testGroupStarting( GroupInfo const& ) override {} void testCaseStarting( TestCaseInfo const& ) override {} void sectionStarting( SectionInfo const& sectionInfo ) override { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); std::shared_ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) m_rootSection = std::make_shared( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); auto it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { node = std::make_shared( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); m_deepestSection = std::move(node); } void assertionStarting(AssertionInfo const&) override {} bool assertionEnded(AssertionStats const& assertionStats) override { assert(!m_sectionStack.empty()); // AssertionResult holds a pointer to a temporary DecomposedExpression, // which getExpandedExpression() calls to build the expression string. // Our section stack copy of the assertionResult will likely outlive the // temporary, so it must be expanded or discarded now to avoid calling // a destroyed object later. prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back(assertionStats); return true; } void sectionEnded(SectionStats const& sectionStats) override { assert(!m_sectionStack.empty()); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } void testCaseEnded(TestCaseStats const& testCaseStats) override { auto node = std::make_shared(testCaseStats); assert(m_sectionStack.size() == 0); node->children.push_back(m_rootSection); m_testCases.push_back(node); m_rootSection.reset(); assert(m_deepestSection); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } void testGroupEnded(TestGroupStats const& testGroupStats) override { auto node = std::make_shared(testGroupStats); node->children.swap(m_testCases); m_testGroups.push_back(node); } void testRunEnded(TestRunStats const& testRunStats) override { auto node = std::make_shared(testRunStats); node->children.swap(m_testGroups); m_testRuns.push_back(node); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; void skipTest(TestCaseInfo const&) override {} IConfigPtr m_config; std::ostream& stream; std::vector m_assertions; std::vector>> m_sections; std::vector> m_testCases; std::vector> m_testGroups; std::vector> m_testRuns; std::shared_ptr m_rootSection; std::shared_ptr m_deepestSection; std::vector> m_sectionStack; ReporterPreferences m_reporterPrefs; }; template char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; } struct TestEventListenerBase : StreamingReporterBase { TestEventListenerBase( ReporterConfig const& _config ); void assertionStarting(AssertionInfo const&) override; bool assertionEnded(AssertionStats const&) override; }; } // end namespace Catch // end catch_reporter_bases.hpp // start catch_console_colour.h namespace Catch { struct Colour { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White, // By intention FileName = LightGrey, Warning = Yellow, ResultError = BrightRed, ResultSuccess = BrightGreen, ResultExpectedFailure = Warning, Error = BrightRed, Success = Green, OriginalExpression = Cyan, ReconstructedExpression = Yellow, SecondaryText = LightGrey, Headers = White }; // Use constructed object for RAII guard Colour( Code _colourCode ); Colour( Colour&& other ) noexcept; Colour& operator=( Colour&& other ) noexcept; ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: bool m_moved = false; }; std::ostream& operator << ( std::ostream& os, Colour const& ); } // end namespace Catch // end catch_console_colour.h // start catch_reporter_registrars.hpp namespace Catch { template class ReporterRegistrar { class ReporterFactory : public IReporterFactory { virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { return std::unique_ptr( new T( config ) ); } virtual std::string getDescription() const override { return T::getDescription(); } }; public: ReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, std::make_shared() ); } }; template class ListenerRegistrar { class ListenerFactory : public IReporterFactory { virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { return std::unique_ptr( new T( config ) ); } virtual std::string getDescription() const override { return std::string(); } }; public: ListenerRegistrar() { getMutableRegistryHub().registerListener( std::make_shared() ); } }; } #if !defined(CATCH_CONFIG_DISABLE) #define CATCH_REGISTER_REPORTER( name, reporterType ) \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #define CATCH_REGISTER_LISTENER( listenerType ) \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #else // CATCH_CONFIG_DISABLE #define CATCH_REGISTER_REPORTER(name, reporterType) #define CATCH_REGISTER_LISTENER(listenerType) #endif // CATCH_CONFIG_DISABLE // end catch_reporter_registrars.hpp // end catch_external_interfaces.h #endif #ifdef CATCH_IMPL // start catch_impl.hpp #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif // Keep these here for external reporters // start catch_test_case_tracker.h #include #include #include namespace Catch { namespace TestCaseTracking { struct NameAndLocation { std::string name; SourceLineInfo location; NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); }; struct ITracker; using ITrackerPtr = std::shared_ptr; struct ITracker { virtual ~ITracker(); // static queries virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; virtual ITracker& parent() = 0; // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( ITrackerPtr const& child ) = 0; virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking virtual bool isSectionTracker() const = 0; virtual bool isIndexTracker() const = 0; }; class TrackerContext { enum RunState { NotStarted, Executing, CompletedCycle }; ITrackerPtr m_rootTracker; ITracker* m_currentTracker = nullptr; RunState m_runState = NotStarted; public: static TrackerContext& instance(); ITracker& startRun(); void endRun(); void startCycle(); void completeCycle(); bool completedCycle() const; ITracker& currentTracker(); void setCurrentTracker( ITracker* tracker ); }; class TrackerBase : public ITracker { protected: enum CycleState { NotStarted, Executing, ExecutingChildren, NeedsAnotherRun, CompletedSuccessfully, Failed }; class TrackerHasName { NameAndLocation m_nameAndLocation; public: TrackerHasName( NameAndLocation const& nameAndLocation ); bool operator ()( ITrackerPtr const& tracker ) const; }; using Children = std::vector; NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState = NotStarted; public: TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); NameAndLocation const& nameAndLocation() const override; bool isComplete() const override; bool isSuccessfullyCompleted() const override; bool isOpen() const override; bool hasChildren() const override; void addChild( ITrackerPtr const& child ) override; ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; ITracker& parent() override; void openChild() override; bool isSectionTracker() const override; bool isIndexTracker() const override; void open(); void close() override; void fail() override; void markAsNeedingAnotherRun() override; private: void moveToParent(); void moveToThis(); }; class SectionTracker : public TrackerBase { std::vector m_filters; public: SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); bool isSectionTracker() const override; static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); void tryOpen(); void addInitialFilters( std::vector const& filters ); void addNextFilters( std::vector const& filters ); }; class IndexTracker : public TrackerBase { int m_size; int m_index = -1; public: IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); bool isIndexTracker() const override; void close() override; static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); int index() const; void moveNext(); }; } // namespace TestCaseTracking using TestCaseTracking::ITracker; using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; using TestCaseTracking::IndexTracker; } // namespace Catch // end catch_test_case_tracker.h // start catch_leak_detector.h namespace Catch { struct LeakDetector { LeakDetector(); }; } // end catch_leak_detector.h // Cpp files will be included in the single-header file here // start catch_approx.cpp #include #include namespace { // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison bool marginComparison(double lhs, double rhs, double margin) { return (lhs + margin >= rhs) && (rhs + margin >= lhs); } } namespace Catch { namespace Detail { Approx::Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), m_margin( 0.0 ), m_scale( 0.0 ), m_value( value ) {} Approx Approx::custom() { return Approx( 0 ); } std::string Approx::toString() const { std::ostringstream oss; oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; return oss.str(); } bool Approx::equalityComparisonImpl(const double other) const { // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value // Thanks to Richard Harris for his help refining the scaled margin value return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); } } // end namespace Detail std::string StringMaker::convert(Catch::Detail::Approx const& value) { return value.toString(); } } // end namespace Catch // end catch_approx.cpp // start catch_assertionhandler.cpp // start catch_context.h #include namespace Catch { struct IResultCapture; struct IRunner; struct IConfig; using IConfigPtr = std::shared_ptr; struct IContext { virtual ~IContext(); virtual IResultCapture* getResultCapture() = 0; virtual IRunner* getRunner() = 0; virtual IConfigPtr getConfig() const = 0; }; struct IMutableContext : IContext { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; virtual void setConfig( IConfigPtr const& config ) = 0; }; IContext& getCurrentContext(); IMutableContext& getCurrentMutableContext(); void cleanUpContext(); } // end catch_context.h #include namespace Catch { auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { expr.streamReconstructedExpression( os ); return os; } LazyExpression::LazyExpression( bool isNegated ) : m_isNegated( isNegated ) {} LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} LazyExpression::operator bool() const { return m_transientExpression != nullptr; } auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { if( lazyExpr.m_isNegated ) os << "!"; if( lazyExpr ) { if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) os << "(" << *lazyExpr.m_transientExpression << ")"; else os << *lazyExpr.m_transientExpression; } else { os << "{** error - unchecked empty expression requested **}"; } return os; } AssertionHandler::AssertionHandler ( StringRef macroName, SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } { getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); } AssertionHandler::~AssertionHandler() { if ( m_inExceptionGuard ) { handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" ); getCurrentContext().getResultCapture()->exceptionEarlyReported(); } } void AssertionHandler::handle( ITransientExpression const& expr ) { bool negated = isFalseTest( m_assertionInfo.resultDisposition ); bool result = expr.getResult() != negated; handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); } void AssertionHandler::handle( ResultWas::OfType resultType ) { handle( resultType, nullptr, false ); } void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { AssertionResultData data( resultType, LazyExpression( false ) ); data.message = message; handle( data, nullptr ); } void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { AssertionResultData data( resultType, LazyExpression( negated ) ); handle( data, expr ); } void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { getResultCapture().assertionRun(); AssertionResult assertionResult{ m_assertionInfo, resultData }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; getResultCapture().assertionEnded( assertionResult ); if( !assertionResult.isOk() ) { m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); m_shouldThrow = getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal); } } auto AssertionHandler::allowThrows() const -> bool { return getCurrentContext().getConfig()->allowThrows(); } auto AssertionHandler::shouldDebugBreak() const -> bool { return m_shouldDebugBreak; } void AssertionHandler::reactWithDebugBreak() const { if (m_shouldDebugBreak) { /////////////////////////////////////////////////////////////////// // To inspect the state during test, you need to go one level up the callstack // To go back to the test and change execution, jump over the reactWithoutDebugBreak() call /////////////////////////////////////////////////////////////////// CATCH_BREAK_INTO_DEBUGGER(); } reactWithoutDebugBreak(); } void AssertionHandler::reactWithoutDebugBreak() const { if( m_shouldThrow ) throw Catch::TestFailureException(); } void AssertionHandler::useActiveException() { handle( ResultWas::ThrewException, Catch::translateActiveException() ); } void AssertionHandler::setExceptionGuard() { assert( m_inExceptionGuard == false ); m_inExceptionGuard = true; } void AssertionHandler::unsetExceptionGuard() { assert( m_inExceptionGuard == true ); m_inExceptionGuard = false; } // This is the overload that takes a string and infers the Equals matcher from it // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); } } // namespace Catch // end catch_assertionhandler.cpp // start catch_assertionresult.cpp namespace Catch { AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): lazyExpression(_lazyExpression), resultType(_resultType) {} std::string AssertionResultData::reconstructExpression() const { if( reconstructedExpression.empty() ) { if( lazyExpression ) { // !TBD Use stringstream for now, but rework above to pass stream in std::ostringstream oss; oss << lazyExpression; reconstructedExpression = oss.str(); } } return reconstructedExpression; } AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) : m_info( info ), m_resultData( data ) {} // Result was a success bool AssertionResult::succeeded() const { return Catch::isOk( m_resultData.resultType ); } // Result was a success, or failure is suppressed bool AssertionResult::isOk() const { return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); } ResultWas::OfType AssertionResult::getResultType() const { return m_resultData.resultType; } bool AssertionResult::hasExpression() const { return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) return "!(" + std::string(m_info.capturedExpression) + ")"; else return m_info.capturedExpression; } std::string AssertionResult::getExpressionInMacro() const { std::string expr; if( m_info.macroName[0] == 0 ) expr = m_info.capturedExpression; else { expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); expr += m_info.macroName; expr += "( "; expr += m_info.capturedExpression; expr += " )"; } return expr; } bool AssertionResult::hasExpandedExpression() const { return hasExpression() && getExpandedExpression() != getExpression(); } std::string AssertionResult::getExpandedExpression() const { std::string expr = m_resultData.reconstructExpression(); return expr.empty() ? getExpression() : expr; } std::string AssertionResult::getMessage() const { return m_resultData.message; } SourceLineInfo AssertionResult::getSourceInfo() const { return m_info.lineInfo; } std::string AssertionResult::getTestMacroName() const { return m_info.macroName; } } // end namespace Catch // end catch_assertionresult.cpp // start catch_benchmark.cpp namespace Catch { auto BenchmarkLooper::getResolution() -> uint64_t { return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); } void BenchmarkLooper::reportStart() { getResultCapture().benchmarkStarting( { m_name } ); } auto BenchmarkLooper::needsMoreIterations() -> bool { auto elapsed = m_timer.getElapsedNanoseconds(); // Exponentially increasing iterations until we're confident in our timer resolution if( elapsed < m_resolution ) { m_iterationsToRun *= 10; return true; } getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); return false; } } // end namespace Catch // end catch_benchmark.cpp // start catch_capture_matchers.cpp namespace Catch { using StringMatcher = Matchers::Impl::MatcherBase; // This is the general overload that takes a any string matcher // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers // the Equals matcher (so the header does not mention matchers) void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { std::string exceptionMessage = Catch::translateActiveException(); MatchExpr expr( exceptionMessage, matcher, matcherString ); handler.handle( expr ); } } // namespace Catch // end catch_capture_matchers.cpp // start catch_commandline.cpp // start catch_commandline.h // start catch_clara.h // Use Catch's value for console width (store Clara's off to the side, if present) #ifdef CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #endif #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wexit-time-destructors" #pragma clang diagnostic ignored "-Wshadow" #endif // start clara.hpp // v1.0-develop.2 // See https://github.com/philsquared/Clara #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 #endif #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH #endif // ----------- #included from clara_textflow.hpp ----------- // TextFlowCpp // // A single-header library for wrapping and laying out basic text, by Phil Nash // // This work is licensed under the BSD 2-Clause license. // See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause // // This project is hosted at https://github.com/philsquared/textflowcpp #include #include #include #include #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { namespace clara { namespace TextFlow { inline auto isWhitespace( char c ) -> bool { static std::string chars = " \t\n\r"; return chars.find( c ) != std::string::npos; } inline auto isBreakableBefore( char c ) -> bool { static std::string chars = "[({<|"; return chars.find( c ) != std::string::npos; } inline auto isBreakableAfter( char c ) -> bool { static std::string chars = "])}>.,:;*+-=&/\\"; return chars.find( c ) != std::string::npos; } class Columns; class Column { std::vector m_strings; size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; size_t m_indent = 0; size_t m_initialIndent = std::string::npos; public: class iterator { friend Column; Column const& m_column; size_t m_stringIndex = 0; size_t m_pos = 0; size_t m_len = 0; size_t m_end = 0; bool m_suffix = false; iterator( Column const& column, size_t stringIndex ) : m_column( column ), m_stringIndex( stringIndex ) {} auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } auto isBoundary( size_t at ) const -> bool { assert( at > 0 ); assert( at <= line().size() ); return at == line().size() || ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || isBreakableBefore( line()[at] ) || isBreakableAfter( line()[at-1] ); } void calcLength() { assert( m_stringIndex < m_column.m_strings.size() ); m_suffix = false; auto width = m_column.m_width-indent(); m_end = m_pos; while( m_end < line().size() && line()[m_end] != '\n' ) ++m_end; if( m_end < m_pos + width ) { m_len = m_end - m_pos; } else { size_t len = width; while (len > 0 && !isBoundary(m_pos + len)) --len; while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) --len; if (len > 0) { m_len = len; } else { m_suffix = true; m_len = width - 1; } } } auto indent() const -> size_t { auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } auto addIndentAndSuffix(std::string const &plain) const -> std::string { return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); } public: explicit iterator( Column const& column ) : m_column( column ) { assert( m_column.m_width > m_column.m_indent ); assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); if( m_len == 0 ) m_stringIndex++; // Empty string } auto operator *() const -> std::string { assert( m_stringIndex < m_column.m_strings.size() ); assert( m_pos <= m_end ); if( m_pos + m_column.m_width < m_end ) return addIndentAndSuffix(line().substr(m_pos, m_len)); else return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); } auto operator ++() -> iterator& { m_pos += m_len; if( m_pos < line().size() && line()[m_pos] == '\n' ) m_pos += 1; else while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) ++m_pos; if( m_pos == line().size() ) { m_pos = 0; ++m_stringIndex; } if( m_stringIndex < m_column.m_strings.size() ) calcLength(); return *this; } auto operator ++(int) -> iterator { iterator prev( *this ); operator++(); return prev; } auto operator ==( iterator const& other ) const -> bool { return m_pos == other.m_pos && m_stringIndex == other.m_stringIndex && &m_column == &other.m_column; } auto operator !=( iterator const& other ) const -> bool { return !operator==( other ); } }; using const_iterator = iterator; explicit Column( std::string const& text ) { m_strings.push_back( text ); } auto width( size_t newWidth ) -> Column& { assert( newWidth > 0 ); m_width = newWidth; return *this; } auto indent( size_t newIndent ) -> Column& { m_indent = newIndent; return *this; } auto initialIndent( size_t newIndent ) -> Column& { m_initialIndent = newIndent; return *this; } auto width() const -> size_t { return m_width; } auto begin() const -> iterator { return iterator( *this ); } auto end() const -> iterator { return { *this, m_strings.size() }; } inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { bool first = true; for( auto line : col ) { if( first ) first = false; else os << "\n"; os << line; } return os; } auto operator + ( Column const& other ) -> Columns; auto toString() const -> std::string { std::ostringstream oss; oss << *this; return oss.str(); } }; class Spacer : public Column { public: explicit Spacer( size_t spaceWidth ) : Column( "" ) { width( spaceWidth ); } }; class Columns { std::vector m_columns; public: class iterator { friend Columns; struct EndTag {}; std::vector const& m_columns; std::vector m_iterators; size_t m_activeIterators; iterator( Columns const& columns, EndTag ) : m_columns( columns.m_columns ), m_activeIterators( 0 ) { m_iterators.reserve( m_columns.size() ); for( auto const& col : m_columns ) m_iterators.push_back( col.end() ); } public: explicit iterator( Columns const& columns ) : m_columns( columns.m_columns ), m_activeIterators( m_columns.size() ) { m_iterators.reserve( m_columns.size() ); for( auto const& col : m_columns ) m_iterators.push_back( col.begin() ); } auto operator ==( iterator const& other ) const -> bool { return m_iterators == other.m_iterators; } auto operator !=( iterator const& other ) const -> bool { return m_iterators != other.m_iterators; } auto operator *() const -> std::string { std::string row, padding; for( size_t i = 0; i < m_columns.size(); ++i ) { auto width = m_columns[i].width(); if( m_iterators[i] != m_columns[i].end() ) { std::string col = *m_iterators[i]; row += padding + col; if( col.size() < width ) padding = std::string( width - col.size(), ' ' ); else padding = ""; } else { padding += std::string( width, ' ' ); } } return row; } auto operator ++() -> iterator& { for( size_t i = 0; i < m_columns.size(); ++i ) { if (m_iterators[i] != m_columns[i].end()) ++m_iterators[i]; } return *this; } auto operator ++(int) -> iterator { iterator prev( *this ); operator++(); return prev; } }; using const_iterator = iterator; auto begin() const -> iterator { return iterator( *this ); } auto end() const -> iterator { return { *this, iterator::EndTag() }; } auto operator += ( Column const& col ) -> Columns& { m_columns.push_back( col ); return *this; } auto operator + ( Column const& col ) -> Columns { Columns combined = *this; combined += col; return combined; } inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { bool first = true; for( auto line : cols ) { if( first ) first = false; else os << "\n"; os << line; } return os; } auto toString() const -> std::string { std::ostringstream oss; oss << *this; return oss.str(); } }; inline auto Column::operator + ( Column const& other ) -> Columns { Columns cols; cols += *this; cols += other; return cols; } }}} // namespace Catch::clara::TextFlow // ----------- end of #include from clara_textflow.hpp ----------- // ........... back in clara.hpp #include #include #include #if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) #define CATCH_PLATFORM_WINDOWS #endif namespace Catch { namespace clara { namespace detail { // Traits for extracting arg and return type of lambdas (for single argument lambdas) template struct UnaryLambdaTraits : UnaryLambdaTraits {}; template struct UnaryLambdaTraits { static const bool isValid = false; }; template struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = typename std::remove_const::type>::type;; using ReturnType = ReturnT; }; class TokenStream; // Transport for raw args (copied from main args, or supplied via init list for testing) class Args { friend TokenStream; std::string m_exeName; std::vector m_args; public: Args( int argc, char *argv[] ) { m_exeName = argv[0]; for( int i = 1; i < argc; ++i ) m_args.push_back( argv[i] ); } Args( std::initializer_list args ) : m_exeName( *args.begin() ), m_args( args.begin()+1, args.end() ) {} auto exeName() const -> std::string { return m_exeName; } }; // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string // may encode an option + its argument if the : or = form is used enum class TokenType { Option, Argument }; struct Token { TokenType type; std::string token; }; inline auto isOptPrefix( char c ) -> bool { return c == '-' #ifdef CATCH_PLATFORM_WINDOWS || c == '/' #endif ; } // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled class TokenStream { using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; void loadBuffer() { m_tokenBuffer.resize( 0 ); // Skip any empty strings while( it != itEnd && it->empty() ) ++it; if( it != itEnd ) { auto const &next = *it; if( isOptPrefix( next[0] ) ) { auto delimiterPos = next.find_first_of( " :=" ); if( delimiterPos != std::string::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); } else { if( next[1] != '-' && next.size() > 2 ) { std::string opt = "- "; for( size_t i = 1; i < next.size(); ++i ) { opt[1] = next[i]; m_tokenBuffer.push_back( { TokenType::Option, opt } ); } } else { m_tokenBuffer.push_back( { TokenType::Option, next } ); } } } else { m_tokenBuffer.push_back( { TokenType::Argument, next } ); } } } public: explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { loadBuffer(); } explicit operator bool() const { return !m_tokenBuffer.empty() || it != itEnd; } auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto operator*() const -> Token { assert( !m_tokenBuffer.empty() ); return m_tokenBuffer.front(); } auto operator->() const -> Token const * { assert( !m_tokenBuffer.empty() ); return &m_tokenBuffer.front(); } auto operator++() -> TokenStream & { if( m_tokenBuffer.size() >= 2 ) { m_tokenBuffer.erase( m_tokenBuffer.begin() ); } else { if( it != itEnd ) ++it; loadBuffer(); } return *this; } }; class ResultBase { public: enum Type { Ok, LogicError, RuntimeError }; protected: ResultBase( Type type ) : m_type( type ) {} virtual ~ResultBase() = default; virtual void enforceOk() const = 0; Type m_type; }; template class ResultValueBase : public ResultBase { public: auto value() const -> T const & { enforceOk(); return m_value; } protected: ResultValueBase( Type type ) : ResultBase( type ) {} ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { if( m_type == ResultBase::Ok ) new( &m_value ) T( other.m_value ); } ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { new( &m_value ) T( value ); } auto operator=( ResultValueBase const &other ) -> ResultValueBase & { if( m_type == ResultBase::Ok ) m_value.~T(); ResultBase::operator=(other); if( m_type == ResultBase::Ok ) new( &m_value ) T( other.m_value ); return *this; } ~ResultValueBase() { if( m_type == Ok ) m_value.~T(); } union { T m_value; }; }; template<> class ResultValueBase : public ResultBase { protected: using ResultBase::ResultBase; }; template class BasicResult : public ResultValueBase { public: template explicit BasicResult( BasicResult const &other ) : ResultValueBase( other.type() ), m_errorMessage( other.errorMessage() ) { assert( type() != ResultBase::Ok ); } template static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } static auto ok() -> BasicResult { return { ResultBase::Ok }; } static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } explicit operator bool() const { return m_type == ResultBase::Ok; } auto type() const -> ResultBase::Type { return m_type; } auto errorMessage() const -> std::string { return m_errorMessage; } protected: virtual void enforceOk() const { // !TBD: If no exceptions, std::terminate here or something switch( m_type ) { case ResultBase::LogicError: throw std::logic_error( m_errorMessage ); case ResultBase::RuntimeError: throw std::runtime_error( m_errorMessage ); case ResultBase::Ok: break; } } std::string m_errorMessage; // Only populated if resultType is an error BasicResult( ResultBase::Type type, std::string const &message ) : ResultValueBase(type), m_errorMessage(message) { assert( m_type != ResultBase::Ok ); } using ResultValueBase::ResultValueBase; using ResultBase::m_type; }; enum class ParseResultType { Matched, NoMatch, ShortCircuitAll, ShortCircuitSame }; class ParseState { public: ParseState( ParseResultType type, TokenStream const &remainingTokens ) : m_type(type), m_remainingTokens( remainingTokens ) {} auto type() const -> ParseResultType { return m_type; } auto remainingTokens() const -> TokenStream { return m_remainingTokens; } private: ParseResultType m_type; TokenStream m_remainingTokens; }; using Result = BasicResult; using ParserResult = BasicResult; using InternalParseResult = BasicResult; struct HelpColumns { std::string left; std::string right; }; template inline auto convertInto( std::string const &source, T& target ) -> ParserResult { std::stringstream ss; ss << source; ss >> target; if( ss.fail() ) return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); else return ParserResult::ok( ParseResultType::Matched ); } inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { target = source; return ParserResult::ok( ParseResultType::Matched ); } inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { std::string srcLC = source; std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") target = false; else return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); return ParserResult::ok( ParseResultType::Matched ); } struct BoundRefBase { BoundRefBase() = default; BoundRefBase( BoundRefBase const & ) = delete; BoundRefBase( BoundRefBase && ) = delete; BoundRefBase &operator=( BoundRefBase const & ) = delete; BoundRefBase &operator=( BoundRefBase && ) = delete; virtual ~BoundRefBase() = default; virtual auto isFlag() const -> bool = 0; virtual auto isContainer() const -> bool { return false; } virtual auto setValue( std::string const &arg ) -> ParserResult = 0; virtual auto setFlag( bool flag ) -> ParserResult = 0; }; struct BoundValueRefBase : BoundRefBase { auto isFlag() const -> bool override { return false; } auto setFlag( bool ) -> ParserResult override { return ParserResult::logicError( "Flags can only be set on boolean fields" ); } }; struct BoundFlagRefBase : BoundRefBase { auto isFlag() const -> bool override { return true; } auto setValue( std::string const &arg ) -> ParserResult override { bool flag; auto result = convertInto( arg, flag ); if( result ) setFlag( flag ); return result; } }; template struct BoundRef : BoundValueRefBase { T &m_ref; explicit BoundRef( T &ref ) : m_ref( ref ) {} auto setValue( std::string const &arg ) -> ParserResult override { return convertInto( arg, m_ref ); } }; template struct BoundRef> : BoundValueRefBase { std::vector &m_ref; explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} auto isContainer() const -> bool override { return true; } auto setValue( std::string const &arg ) -> ParserResult override { T temp; auto result = convertInto( arg, temp ); if( result ) m_ref.push_back( temp ); return result; } }; struct BoundFlagRef : BoundFlagRefBase { bool &m_ref; explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} auto setFlag( bool flag ) -> ParserResult override { m_ref = flag; return ParserResult::ok( ParseResultType::Matched ); } }; template struct LambdaInvoker { static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); template static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { return lambda( arg ); } }; template<> struct LambdaInvoker { template static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { lambda( arg ); return ParserResult::ok( ParseResultType::Matched ); } }; template inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { ArgType temp; auto result = convertInto( arg, temp ); return !result ? result : LambdaInvoker::ReturnType>::invoke( lambda, temp ); }; template struct BoundLambda : BoundValueRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} auto setValue( std::string const &arg ) -> ParserResult override { return invokeLambda::ArgType>( m_lambda, arg ); } }; template struct BoundFlagLambda : BoundFlagRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} auto setFlag( bool flag ) -> ParserResult override { return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); } }; enum class Optionality { Optional, Required }; struct Parser; class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } auto parse( Args const &args ) const -> InternalParseResult { return parse( args.exeName(), TokenStream( args ) ); } }; template class ComposableParserImpl : public ParserBase { public: template auto operator|( T const &other ) const -> Parser; }; // Common code and state for Args and Opts template class ParserRefImpl : public ComposableParserImpl { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; std::string m_hint; std::string m_description; explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} public: template ParserRefImpl( T &ref, std::string const &hint ) : m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ParserRefImpl( LambdaT const &ref, std::string const &hint ) : m_ref( std::make_shared>( ref ) ), m_hint(hint) {} auto operator()( std::string const &description ) -> DerivedT & { m_description = description; return static_cast( *this ); } auto optional() -> DerivedT & { m_optionality = Optionality::Optional; return static_cast( *this ); }; auto required() -> DerivedT & { m_optionality = Optionality::Required; return static_cast( *this ); }; auto isOptional() const -> bool { return m_optionality == Optionality::Optional; } auto cardinality() const -> size_t override { if( m_ref->isContainer() ) return 0; else return 1; } auto hint() const -> std::string { return m_hint; } }; class ExeName : public ComposableParserImpl { std::shared_ptr m_name; std::shared_ptr m_ref; template static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { return std::make_shared>( lambda) ; } public: ExeName() : m_name( std::make_shared( "" ) ) {} explicit ExeName( std::string &ref ) : ExeName() { m_ref = std::make_shared>( ref ); } template explicit ExeName( LambdaT const& lambda ) : ExeName() { m_ref = std::make_shared>( lambda ); } // The exe name is not parsed out of the normal tokens, but is handled specially auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); } auto name() const -> std::string { return *m_name; } auto set( std::string const& newName ) -> ParserResult { auto lastSlash = newName.find_last_of( "\\/" ); auto filename = ( lastSlash == std::string::npos ) ? newName : newName.substr( lastSlash+1 ); *m_name = filename; if( m_ref ) return m_ref->setValue( filename ); else return ParserResult::ok( ParseResultType::Matched ); } }; class Arg : public ParserRefImpl { public: using ParserRefImpl::ParserRefImpl; auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); if( !validationResult ) return InternalParseResult( validationResult ); auto remainingTokens = tokens; auto const &token = *remainingTokens; if( token.type != TokenType::Argument ) return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); auto result = m_ref->setValue( remainingTokens->token ); if( !result ) return InternalParseResult( result ); else return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } }; inline auto normaliseOpt( std::string const &optName ) -> std::string { #ifdef CATCH_PLATFORM_WINDOWS if( optName[0] == '/' ) return "-" + optName.substr( 1 ); else #endif return optName; } class Opt : public ParserRefImpl { protected: std::vector m_optNames; public: template explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} template Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} template Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} auto operator[]( std::string const &optName ) -> Opt & { m_optNames.push_back( optName ); return *this; } auto getHelpColumns() const -> std::vector { std::ostringstream oss; bool first = true; for( auto const &opt : m_optNames ) { if (first) first = false; else oss << ", "; oss << opt; } if( !m_hint.empty() ) oss << " <" << m_hint << ">"; return { { oss.str(), m_description } }; } auto isMatch( std::string const &optToken ) const -> bool { auto normalisedToken = normaliseOpt( optToken ); for( auto const &name : m_optNames ) { if( normaliseOpt( name ) == normalisedToken ) return true; } return false; } using ParserBase::parse; auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); if( !validationResult ) return InternalParseResult( validationResult ); auto remainingTokens = tokens; if( remainingTokens && remainingTokens->type == TokenType::Option ) { auto const &token = *remainingTokens; if( isMatch(token.token ) ) { if( m_ref->isFlag() ) { auto result = m_ref->setFlag( true ); if( !result ) return InternalParseResult( result ); if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } else { ++remainingTokens; if( !remainingTokens ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto const &argToken = *remainingTokens; if( argToken.type != TokenType::Argument ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto result = m_ref->setValue( argToken.token ); if( !result ) return InternalParseResult( result ); if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } } return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); } auto validate() const -> Result override { if( m_optNames.empty() ) return Result::logicError( "No options supplied to Opt" ); for( auto const &name : m_optNames ) { if( name.empty() ) return Result::logicError( "Option name cannot be empty" ); #ifdef CATCH_PLATFORM_WINDOWS if( name[0] != '-' && name[0] != '/' ) return Result::logicError( "Option name must begin with '-' or '/'" ); #else if( name[0] != '-' ) return Result::logicError( "Option name must begin with '-'" ); #endif } return ParserRefImpl::validate(); } }; struct Help : Opt { Help( bool &showHelpFlag ) : Opt([&]( bool flag ) { showHelpFlag = flag; return ParserResult::ok( ParseResultType::ShortCircuitAll ); }) { static_cast( *this ) ("display usage information") ["-?"]["-h"]["--help"] .optional(); } }; struct Parser : ParserBase { mutable ExeName m_exeName; std::vector m_options; std::vector m_args; auto operator|=( ExeName const &exeName ) -> Parser & { m_exeName = exeName; return *this; } auto operator|=( Arg const &arg ) -> Parser & { m_args.push_back(arg); return *this; } auto operator|=( Opt const &opt ) -> Parser & { m_options.push_back(opt); return *this; } auto operator|=( Parser const &other ) -> Parser & { m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); return *this; } template auto operator|( T const &other ) const -> Parser { return Parser( *this ) |= other; } auto getHelpColumns() const -> std::vector { std::vector cols; for (auto const &o : m_options) { auto childCols = o.getHelpColumns(); cols.insert( cols.end(), childCols.begin(), childCols.end() ); } return cols; } void writeToStream( std::ostream &os ) const { if (!m_exeName.name().empty()) { os << "usage:\n" << " " << m_exeName.name() << " "; bool required = true, first = true; for( auto const &arg : m_args ) { if (first) first = false; else os << " "; if( arg.isOptional() && required ) { os << "["; required = false; } os << "<" << arg.hint() << ">"; if( arg.cardinality() == 0 ) os << " ... "; } if( !required ) os << "]"; if( !m_options.empty() ) os << " options"; os << "\n\nwhere options are:" << std::endl; } auto rows = getHelpColumns(); size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; size_t optWidth = 0; for( auto const &cols : rows ) optWidth = (std::max)(optWidth, cols.left.size() + 2); for( auto const &cols : rows ) { auto row = TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + TextFlow::Spacer(4) + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); os << row << std::endl; } } friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { parser.writeToStream( os ); return os; } auto validate() const -> Result override { for( auto const &opt : m_options ) { auto result = opt.validate(); if( !result ) return result; } for( auto const &arg : m_args ) { auto result = arg.validate(); if( !result ) return result; } return Result::ok(); } using ParserBase::parse; auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { struct ParserInfo { ParserBase const* parser = nullptr; size_t count = 0; }; const size_t totalParsers = m_options.size() + m_args.size(); assert( totalParsers < 512 ); // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do ParserInfo parseInfos[512]; { size_t i = 0; for (auto const &opt : m_options) parseInfos[i++].parser = &opt; for (auto const &arg : m_args) parseInfos[i++].parser = &arg; } m_exeName.set( exeName ); auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); while( result.value().remainingTokens() ) { bool tokenParsed = false; for( size_t i = 0; i < totalParsers; ++i ) { auto& parseInfo = parseInfos[i]; if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); if (!result) return result; if (result.value().type() != ParseResultType::NoMatch) { tokenParsed = true; ++parseInfo.count; break; } } } if( result.value().type() == ParseResultType::ShortCircuitAll ) return result; if( !tokenParsed ) return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); } // !TBD Check missing required options return result; } }; template template auto ComposableParserImpl::operator|( T const &other ) const -> Parser { return Parser() | static_cast( *this ) | other; } } // namespace detail // A Combined parser using detail::Parser; // A parser for options using detail::Opt; // A parser for arguments using detail::Arg; // Wrapper for argc, argv from main() using detail::Args; // Specifies the name of the executable using detail::ExeName; // Convenience wrapper for option parser that specifies the help option using detail::Help; // enum of result types from a parse using detail::ParseResultType; // Result type for parser operation using detail::ParserResult; }} // namespace Catch::clara // end clara.hpp #ifdef __clang__ #pragma clang diagnostic pop #endif // Restore Clara's value for console width, if present #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #endif // end catch_clara.h namespace Catch { clara::Parser makeCommandLineParser( ConfigData& config ); } // end namespace Catch // end catch_commandline.h #include #include namespace Catch { clara::Parser makeCommandLineParser( ConfigData& config ) { using namespace clara; auto const setWarning = [&]( std::string const& warning ) { if( warning != "NoAssertions" ) return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); return ParserResult::ok( ParseResultType::Matched ); }; auto const loadTestNamesFromFile = [&]( std::string const& filename ) { std::ifstream f( filename.c_str() ); if( !f.is_open() ) return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); std::string line; while( std::getline( f, line ) ) { line = trim(line); if( !line.empty() && !startsWith( line, '#' ) ) { if( !startsWith( line, '"' ) ) line = '"' + line + '"'; config.testsOrTags.push_back( line + ',' ); } } return ParserResult::ok( ParseResultType::Matched ); }; auto const setTestOrder = [&]( std::string const& order ) { if( startsWith( "declared", order ) ) config.runOrder = RunTests::InDeclarationOrder; else if( startsWith( "lexical", order ) ) config.runOrder = RunTests::InLexicographicalOrder; else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); return ParserResult::ok( ParseResultType::Matched ); }; auto const setRngSeed = [&]( std::string const& seed ) { if( seed != "time" ) return clara::detail::convertInto( seed, config.rngSeed ); config.rngSeed = static_cast( std::time(nullptr) ); return ParserResult::ok( ParseResultType::Matched ); }; auto const setColourUsage = [&]( std::string const& useColour ) { auto mode = toLower( useColour ); if( mode == "yes" ) config.useColour = UseColour::Yes; else if( mode == "no" ) config.useColour = UseColour::No; else if( mode == "auto" ) config.useColour = UseColour::Auto; else return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); return ParserResult::ok( ParseResultType::Matched ); }; auto const setWaitForKeypress = [&]( std::string const& keypress ) { auto keypressLc = toLower( keypress ); if( keypressLc == "start" ) config.waitForKeypress = WaitForKeypress::BeforeStart; else if( keypressLc == "exit" ) config.waitForKeypress = WaitForKeypress::BeforeExit; else if( keypressLc == "both" ) config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; else return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); return ParserResult::ok( ParseResultType::Matched ); }; auto const setVerbosity = [&]( std::string const& verbosity ) { auto lcVerbosity = toLower( verbosity ); if( lcVerbosity == "quiet" ) config.verbosity = Verbosity::Quiet; else if( lcVerbosity == "normal" ) config.verbosity = Verbosity::Normal; else if( lcVerbosity == "high" ) config.verbosity = Verbosity::High; else return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); return ParserResult::ok( ParseResultType::Matched ); }; auto cli = ExeName( config.processName ) | Help( config.showHelp ) | Opt( config.listTests ) ["-l"]["--list-tests"] ( "list all/matching test cases" ) | Opt( config.listTags ) ["-t"]["--list-tags"] ( "list all/matching tags" ) | Opt( config.showSuccessfulTests ) ["-s"]["--success"] ( "include successful tests in output" ) | Opt( config.shouldDebugBreak ) ["-b"]["--break"] ( "break into debugger on failure" ) | Opt( config.noThrow ) ["-e"]["--nothrow"] ( "skip exception tests" ) | Opt( config.showInvisibles ) ["-i"]["--invisibles"] ( "show invisibles (tabs, newlines)" ) | Opt( config.outputFilename, "filename" ) ["-o"]["--out"] ( "output filename" ) | Opt( config.reporterNames, "name" ) ["-r"]["--reporter"] ( "reporter to use (defaults to console)" ) | Opt( config.name, "name" ) ["-n"]["--name"] ( "suite name" ) | Opt( [&]( bool ){ config.abortAfter = 1; } ) ["-a"]["--abort"] ( "abort at first failure" ) | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) ["-x"]["--abortx"] ( "abort after x failures" ) | Opt( setWarning, "warning name" ) ["-w"]["--warn"] ( "enable warnings" ) | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) ["-d"]["--durations"] ( "show test durations" ) | Opt( loadTestNamesFromFile, "filename" ) ["-f"]["--input-file"] ( "load test names to run from a file" ) | Opt( config.filenamesAsTags ) ["-#"]["--filenames-as-tags"] ( "adds a tag for the filename" ) | Opt( config.sectionsToRun, "section name" ) ["-c"]["--section"] ( "specify section to run" ) | Opt( setVerbosity, "quiet|normal|high" ) ["-v"]["--verbosity"] ( "set output verbosity" ) | Opt( config.listTestNamesOnly ) ["--list-test-names-only"] ( "list all/matching test cases names only" ) | Opt( config.listReporters ) ["--list-reporters"] ( "list all reporters" ) | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] ( "test case order (defaults to decl)" ) | Opt( setRngSeed, "'time'|number" ) ["--rng-seed"] ( "set a specific seed for random numbers" ) | Opt( setColourUsage, "yes|no" ) ["--use-colour"] ( "should output be colourised" ) | Opt( config.libIdentify ) ["--libidentify"] ( "report name and version according to libidentify standard" ) | Opt( setWaitForKeypress, "start|exit|both" ) ["--wait-for-keypress"] ( "waits for a keypress before exiting" ) | Opt( config.benchmarkResolutionMultiple, "multiplier" ) ["--benchmark-resolution-multiple"] ( "multiple of clock resolution to run benchmarks" ) | Arg( config.testsOrTags, "test name|pattern|tags" ) ( "which test or tests to use" ); return cli; } } // end namespace Catch // end catch_commandline.cpp // start catch_common.cpp #include #include namespace Catch { SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept : file( _file ), line( _line ) {} bool SourceLineInfo::empty() const noexcept { return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ os << info.file << '(' << info.line << ')'; #else os << info.file << ':' << info.line; #endif return os; } bool isTrue( bool value ){ return value; } bool alwaysTrue() { return true; } bool alwaysFalse() { return false; } std::string StreamEndStop::operator+() const { return std::string(); } NonCopyable::NonCopyable() = default; NonCopyable::~NonCopyable() = default; } // end catch_common.cpp // start catch_config.cpp namespace Catch { Config::Config( ConfigData const& data ) : m_data( data ), m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); for( auto const& testOrTags : data.testsOrTags ) parser.parse( testOrTags ); m_testSpec = parser.testSpec(); } } std::string const& Config::getFilename() const { return m_data.outputFilename ; } bool Config::listTests() const { return m_data.listTests; } bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool Config::listTags() const { return m_data.listTags; } bool Config::listReporters() const { return m_data.listReporters; } std::string Config::getProcessName() const { return m_data.processName; } std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } TestSpec const& Config::testSpec() const { return m_testSpec; } bool Config::showHelp() const { return m_data.showHelp; } // IConfig interface bool Config::allowThrows() const { return !m_data.noThrow; } std::ostream& Config::stream() const { return m_stream->stream(); } std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } unsigned int Config::rngSeed() const { return m_data.rngSeed; } int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } int Config::abortAfter() const { return m_data.abortAfter; } bool Config::showInvisibles() const { return m_data.showInvisibles; } Verbosity Config::verbosity() const { return m_data.verbosity; } IStream const* Config::openStream() { if( m_data.outputFilename.empty() ) return new CoutStream(); else if( m_data.outputFilename[0] == '%' ) { if( m_data.outputFilename == "%debug" ) return new DebugOutStream(); else CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); } else return new FileStream( m_data.outputFilename ); } } // end namespace Catch // end catch_config.cpp // start catch_console_colour.cpp #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif // start catch_errno_guard.h namespace Catch { class ErrnoGuard { public: ErrnoGuard(); ~ErrnoGuard(); private: int m_oldErrno; }; } // end catch_errno_guard.h // start catch_windows_h_proxy.h #if defined(CATCH_PLATFORM_WINDOWS) #if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) # define CATCH_DEFINED_NOMINMAX # define NOMINMAX #endif #if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) # define CATCH_DEFINED_WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #ifdef __AFXDLL #include #else #include #endif #ifdef CATCH_DEFINED_NOMINMAX # undef NOMINMAX #endif #ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN # undef WIN32_LEAN_AND_MEAN #endif #endif // defined(CATCH_PLATFORM_WINDOWS) // end catch_windows_h_proxy.h namespace Catch { namespace { struct IColourImpl { virtual ~IColourImpl() = default; virtual void use( Colour::Code _colourCode ) = 0; }; struct NoColourImpl : IColourImpl { void use( Colour::Code ) {} static IColourImpl* instance() { static NoColourImpl s_instance; return &s_instance; } }; } // anon namespace } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) # ifdef CATCH_PLATFORM_WINDOWS # define CATCH_CONFIG_COLOUR_WINDOWS # else # define CATCH_CONFIG_COLOUR_ANSI # endif #endif #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// namespace Catch { namespace { class Win32ColourImpl : public IColourImpl { public: Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Red: return setTextAttribute( FOREGROUND_RED ); case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); case Colour::Grey: return setTextAttribute( 0 ); case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } private: void setTextAttribute( WORD _textAttribute ) { SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); } HANDLE stdoutHandle; WORD originalForegroundAttributes; WORD originalBackgroundAttributes; }; IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = UseColour::Yes; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// #include namespace Catch { namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); case Colour::Blue: return setColour( "[0;34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); case Colour::LightGrey: return setColour( "[0;37m" ); case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } static IColourImpl* instance() { static PosixColourImpl s_instance; return &s_instance; } private: void setColour( const char* _escapeCode ) { Catch::cout() << '\033' << _escapeCode; } }; bool useColourOnPlatform() { return #ifdef CATCH_PLATFORM_MAC !isDebuggerActive() && #endif isatty(STDOUT_FILENO); } IColourImpl* platformColourInstance() { ErrnoGuard guard; IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes ? PosixColourImpl::instance() : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } } // end namespace Catch #endif // Windows/ ANSI/ None namespace Catch { Colour::Colour( Code _colourCode ) { use( _colourCode ); } Colour::Colour( Colour&& rhs ) noexcept { m_moved = rhs.m_moved; rhs.m_moved = true; } Colour& Colour::operator=( Colour&& rhs ) noexcept { m_moved = rhs.m_moved; rhs.m_moved = true; return *this; } Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { static IColourImpl* impl = platformColourInstance(); impl->use( _colourCode ); } std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } } // end namespace Catch #if defined(__clang__) # pragma clang diagnostic pop #endif // end catch_console_colour.cpp // start catch_context.cpp namespace Catch { class Context : public IMutableContext, NonCopyable { public: // IContext virtual IResultCapture* getResultCapture() override { return m_resultCapture; } virtual IRunner* getRunner() override { return m_runner; } virtual IConfigPtr getConfig() const override { return m_config; } virtual ~Context() override; public: // IMutableContext virtual void setResultCapture( IResultCapture* resultCapture ) override { m_resultCapture = resultCapture; } virtual void setRunner( IRunner* runner ) override { m_runner = runner; } virtual void setConfig( IConfigPtr const& config ) override { m_config = config; } friend IMutableContext& getCurrentMutableContext(); private: IConfigPtr m_config; IRunner* m_runner = nullptr; IResultCapture* m_resultCapture = nullptr; }; namespace { Context* currentContext = nullptr; } IMutableContext& getCurrentMutableContext() { if( !currentContext ) currentContext = new Context(); return *currentContext; } IContext& getCurrentContext() { return getCurrentMutableContext(); } void cleanUpContext() { delete currentContext; currentContext = nullptr; } IContext::~IContext() = default; IMutableContext::~IMutableContext() = default; Context::~Context() = default; } // end catch_context.cpp // start catch_debug_console.cpp // start catch_debug_console.h #include namespace Catch { void writeToDebugConsole( std::string const& text ); } // end catch_debug_console.h #ifdef CATCH_PLATFORM_WINDOWS namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); } } #else namespace Catch { void writeToDebugConsole( std::string const& text ) { // !TBD: Need a version for Mac/ XCode and other IDEs Catch::cout() << text; } } #endif // Platform // end catch_debug_console.cpp // start catch_debugger.cpp #ifdef CATCH_PLATFORM_MAC #include #include #include #include #include namespace Catch { // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive(){ int mib[4]; struct kinfo_proc info; std::size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } // We're being debugged if the P_TRACED flag is set. return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); } } // namespace Catch #elif defined(CATCH_PLATFORM_LINUX) #include #include namespace Catch{ // The standard POSIX way of detecting a debugger is to attempt to // ptrace() the process, but this needs to be done from a child and not // this process itself to still allow attaching to this process later // if wanted, so is rather heavy. Under Linux we have the PID of the // "debugger" (which doesn't need to be gdb, of course, it could also // be strace, for example) in /proc/$PID/status, so just get it from // there instead. bool isDebuggerActive(){ // Libstdc++ has a bug, where std::ifstream sets errno to 0 // This way our users can properly assert over errno values ErrnoGuard guard; std::ifstream in("/proc/self/status"); for( std::string line; std::getline(in, line); ) { static const int PREFIX_LEN = 11; if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { // We're traced if the PID is not 0 and no other PID starts // with 0 digit, so it's enough to check for just a single // character. return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; } } return false; } } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #else namespace Catch { bool isDebuggerActive() { return false; } } #endif // Platform // end catch_debugger.cpp // start catch_decomposer.cpp namespace Catch { ITransientExpression::~ITransientExpression() = default; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { if( lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && rhs.find('\n') == std::string::npos ) os << lhs << " " << op << " " << rhs; else os << lhs << "\n" << op << "\n" << rhs; } } // end catch_decomposer.cpp // start catch_errno_guard.cpp #include namespace Catch { ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } } // end catch_errno_guard.cpp // start catch_exception_translator_registry.cpp // start catch_exception_translator_registry.h #include #include #include namespace Catch { class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry(); virtual void registerTranslator( const IExceptionTranslator* translator ); virtual std::string translateActiveException() const override; std::string tryTranslators() const; private: std::vector> m_translators; }; } // end catch_exception_translator_registry.h #ifdef __OBJC__ #import "Foundation/Foundation.h" #endif namespace Catch { ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { } void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { m_translators.push_back( std::unique_ptr( translator ) ); } std::string ExceptionTranslatorRegistry::translateActiveException() const { try { #ifdef __OBJC__ // In Objective-C try objective-c exceptions first @try { return tryTranslators(); } @catch (NSException *exception) { return Catch::Detail::stringify( [exception description] ); } #else return tryTranslators(); #endif } catch( TestFailureException& ) { std::rethrow_exception(std::current_exception()); } catch( std::exception& ex ) { return ex.what(); } catch( std::string& msg ) { return msg; } catch( const char* msg ) { return msg; } catch(...) { return "Unknown exception"; } } std::string ExceptionTranslatorRegistry::tryTranslators() const { if( m_translators.empty() ) std::rethrow_exception(std::current_exception()); else return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); } } // end catch_exception_translator_registry.cpp // start catch_fatal_condition.cpp // start catch_fatal_condition.h #include #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { struct FatalConditionHandler { void reset(); }; } # else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { struct FatalConditionHandler { static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); FatalConditionHandler(); static void reset(); ~FatalConditionHandler(); private: static bool isSet; static ULONG guaranteeSize; static PVOID exceptionHandlerHandle; }; } // namespace Catch # endif // CATCH_CONFIG_WINDOWS_SEH #else // Not Windows - assumed to be POSIX compatible ////////////////////////// # if !defined(CATCH_CONFIG_POSIX_SIGNALS) namespace Catch { struct FatalConditionHandler { void reset(); }; } # else // CATCH_CONFIG_POSIX_SIGNALS is defined #include namespace Catch { struct FatalConditionHandler { static bool isSet; static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; static stack_t oldSigStack; static char altStackMem[]; static void handleSignal( int sig ); FatalConditionHandler(); ~FatalConditionHandler(); static void reset(); }; } // namespace Catch # endif // CATCH_CONFIG_POSIX_SIGNALS #endif // not Windows // end catch_fatal_condition.h namespace { // Report the error condition void reportFatal( char const * const message ) { Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); } } #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// # if !defined ( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { void FatalConditionHandler::reset() {} } # else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { struct SignalDefs { DWORD id; const char* name; }; // There is no 1-1 mapping between signals and windows exceptions. // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. static SignalDefs signalDefs[] = { { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, }; LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for (auto const& def : signalDefs) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { reportFatal(def.name); } } // If its not an exception we care about, pass it along. // This stops us from eating debugger breaks etc. return EXCEPTION_CONTINUE_SEARCH; } FatalConditionHandler::FatalConditionHandler() { isSet = true; // 32k seems enough for Catch to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; exceptionHandlerHandle = nullptr; // Register as first handler in current chain exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); } void FatalConditionHandler::reset() { if (isSet) { // Unregister handler and restore the old guarantee RemoveVectoredExceptionHandler(exceptionHandlerHandle); SetThreadStackGuarantee(&guaranteeSize); exceptionHandlerHandle = nullptr; isSet = false; } } FatalConditionHandler::~FatalConditionHandler() { reset(); } bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; } // namespace Catch # endif // CATCH_CONFIG_WINDOWS_SEH #else // Not Windows - assumed to be POSIX compatible ////////////////////////// # if !defined(CATCH_CONFIG_POSIX_SIGNALS) namespace Catch { void FatalConditionHandler::reset() {} } # else // CATCH_CONFIG_POSIX_SIGNALS is defined #include namespace Catch { struct SignalDefs { int id; const char* name; }; static SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, { SIGFPE, "SIGFPE - Floating point error signal" }, { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; void FatalConditionHandler::handleSignal( int sig ) { char const * name = ""; for (auto const& def : signalDefs) { if (sig == def.id) { name = def.name; break; } } reset(); reportFatal(name); raise( sig ); } FatalConditionHandler::FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; sigStack.ss_size = SIGSTKSZ; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { }; sa.sa_handler = handleSignal; sa.sa_flags = SA_ONSTACK; for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } FatalConditionHandler::~FatalConditionHandler() { reset(); } void FatalConditionHandler::reset() { if( isSet ) { // Set signals back to previous values -- hopefully nobody overwrote them in the meantime for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); } // Return the old stack sigaltstack(&oldSigStack, nullptr); isSet = false; } } bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; } // namespace Catch # endif // CATCH_CONFIG_POSIX_SIGNALS #endif // not Windows // end catch_fatal_condition.cpp // start catch_interfaces_capture.cpp namespace Catch { IResultCapture::~IResultCapture() = default; } // end catch_interfaces_capture.cpp // start catch_interfaces_config.cpp namespace Catch { IConfig::~IConfig() = default; } // end catch_interfaces_config.cpp // start catch_interfaces_exception.cpp namespace Catch { IExceptionTranslator::~IExceptionTranslator() = default; IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; } // end catch_interfaces_exception.cpp // start catch_interfaces_registry_hub.cpp namespace Catch { IRegistryHub::~IRegistryHub() = default; IMutableRegistryHub::~IMutableRegistryHub() = default; } // end catch_interfaces_registry_hub.cpp // start catch_interfaces_reporter.cpp // start catch_reporter_multi.h namespace Catch { class MultipleReporters : public IStreamingReporter { using Reporters = std::vector; Reporters m_reporters; public: void add( IStreamingReporterPtr&& reporter ); public: // IStreamingReporter ReporterPreferences getPreferences() const override; void noMatchingTestCases( std::string const& spec ) override; static std::set getSupportedVerbosities(); void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; void testRunStarting( TestRunInfo const& testRunInfo ) override; void testGroupStarting( GroupInfo const& groupInfo ) override; void testCaseStarting( TestCaseInfo const& testInfo ) override; void sectionStarting( SectionInfo const& sectionInfo ) override; void assertionStarting( AssertionInfo const& assertionInfo ) override; // The return value indicates if the messages buffer should be cleared: bool assertionEnded( AssertionStats const& assertionStats ) override; void sectionEnded( SectionStats const& sectionStats ) override; void testCaseEnded( TestCaseStats const& testCaseStats ) override; void testGroupEnded( TestGroupStats const& testGroupStats ) override; void testRunEnded( TestRunStats const& testRunStats ) override; void skipTest( TestCaseInfo const& testInfo ) override; bool isMulti() const override; }; } // end namespace Catch // end catch_reporter_multi.h namespace Catch { ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} std::ostream& ReporterConfig::stream() const { return *m_stream; } IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} GroupInfo::GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ) : name( _name ), groupIndex( _groupIndex ), groupsCounts( _groupsCount ) {} AssertionStats::AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, Totals const& _totals ) : assertionResult( _assertionResult ), infoMessages( _infoMessages ), totals( _totals ) { assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); builder << assertionResult.getMessage(); builder.m_info.message = builder.m_stream.str(); infoMessages.push_back( builder.m_info ); } } AssertionStats::~AssertionStats() = default; SectionStats::SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, bool _missingAssertions ) : sectionInfo( _sectionInfo ), assertions( _assertions ), durationInSeconds( _durationInSeconds ), missingAssertions( _missingAssertions ) {} SectionStats::~SectionStats() = default; TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, bool _aborting ) : testInfo( _testInfo ), totals( _totals ), stdOut( _stdOut ), stdErr( _stdErr ), aborting( _aborting ) {} TestCaseStats::~TestCaseStats() = default; TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, bool _aborting ) : groupInfo( _groupInfo ), totals( _totals ), aborting( _aborting ) {} TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) : groupInfo( _groupInfo ), aborting( false ) {} TestGroupStats::~TestGroupStats() = default; TestRunStats::TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, bool _aborting ) : runInfo( _runInfo ), totals( _totals ), aborting( _aborting ) {} TestRunStats::~TestRunStats() = default; void IStreamingReporter::fatalErrorEncountered( StringRef ) {} bool IStreamingReporter::isMulti() const { return false; } IReporterFactory::~IReporterFactory() = default; IReporterRegistry::~IReporterRegistry() = default; void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { if( !existingReporter ) { existingReporter = std::move( additionalReporter ); return; } MultipleReporters* multi = nullptr; if( existingReporter->isMulti() ) { multi = static_cast( existingReporter.get() ); } else { auto newMulti = std::unique_ptr( new MultipleReporters ); newMulti->add( std::move( existingReporter ) ); multi = newMulti.get(); existingReporter = std::move( newMulti ); } multi->add( std::move( additionalReporter ) ); } } // end namespace Catch // end catch_interfaces_reporter.cpp // start catch_interfaces_runner.cpp namespace Catch { IRunner::~IRunner() = default; } // end catch_interfaces_runner.cpp // start catch_interfaces_testcase.cpp namespace Catch { ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } // end catch_interfaces_testcase.cpp // start catch_leak_detector.cpp namespace Catch { #ifdef CATCH_CONFIG_WINDOWS_CRTDBG #include LeakDetector::LeakDetector() { int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); flag |= _CRTDBG_LEAK_CHECK_DF; flag |= _CRTDBG_ALLOC_MEM_DF; _CrtSetDbgFlag(flag); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); // Change this to leaking allocation's number to break there _CrtSetBreakAlloc(-1); } #else LeakDetector::LeakDetector(){} #endif } // end catch_leak_detector.cpp // start catch_list.cpp // start catch_list.h #include namespace Catch { std::size_t listTests( Config const& config ); std::size_t listTestsNamesOnly( Config const& config ); struct TagInfo { void add( std::string const& spelling ); std::string all() const; std::set spellings; std::size_t count = 0; }; std::size_t listTags( Config const& config ); std::size_t listReporters( Config const& /*config*/ ); Option list( Config const& config ); } // end namespace Catch // end catch_list.h // start catch_text.h namespace Catch { using namespace clara::TextFlow; } // end catch_text.h #include #include #include namespace Catch { std::size_t listTests( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Matching test cases:\n"; else { Catch::cout() << "All available test cases:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( auto const& testCaseInfo : matchedTestCases ) { Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; Colour colourGuard( colour ); Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; if( config.verbosity() >= Verbosity::High ) { Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; std::string description = testCaseInfo.description; if( description.empty() ) description = "(NO DESCRIPTION)"; Catch::cout() << Column( description ).indent(4) << std::endl; } if( !testCaseInfo.tags.empty() ) Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; } if( !config.testSpec().hasFilters() ) Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; else Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; return matchedTestCases.size(); } std::size_t listTestsNamesOnly( Config const& config ) { TestSpec testSpec = config.testSpec(); if( !config.testSpec().hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( auto const& testCaseInfo : matchedTestCases ) { matchedTests++; if( startsWith( testCaseInfo.name, '#' ) ) Catch::cout() << '"' << testCaseInfo.name << '"'; else Catch::cout() << testCaseInfo.name; if ( config.verbosity() >= Verbosity::High ) Catch::cout() << "\t@" << testCaseInfo.lineInfo; Catch::cout() << std::endl; } return matchedTests; } void TagInfo::add( std::string const& spelling ) { ++count; spellings.insert( spelling ); } std::string TagInfo::all() const { std::string out; for( auto const& spelling : spellings ) out += "[" + spelling + "]"; return out; } std::size_t listTags( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { Catch::cout() << "All available tags:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::map tagCounts; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( auto const& testCase : matchedTestCases ) { for( auto const& tagName : testCase.getTestCaseInfo().tags ) { std::string lcaseTagName = toLower( tagName ); auto countIt = tagCounts.find( lcaseTagName ); if( countIt == tagCounts.end() ) countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; countIt->second.add( tagName ); } } for( auto const& tagCount : tagCounts ) { std::ostringstream oss; oss << " " << std::setw(2) << tagCount.second.count << " "; auto wrapper = Column( tagCount.second.all() ) .initialIndent( 0 ) .indent( oss.str().size() ) .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); Catch::cout() << oss.str() << wrapper << '\n'; } Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } std::size_t listReporters( Config const& /*config*/ ) { Catch::cout() << "Available reporters:\n"; IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); std::size_t maxNameLen = 0; for( auto const& factoryKvp : factories ) maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); for( auto const& factoryKvp : factories ) { Catch::cout() << Column( factoryKvp.first + ":" ) .indent(2) .width( 5+maxNameLen ) + Column( factoryKvp.second->getDescription() ) .initialIndent(0) .indent(2) .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) << "\n"; } Catch::cout() << std::endl; return factories.size(); } Option list( Config const& config ) { Option listedCount; if( config.listTests() ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); if( config.listTags() ) listedCount = listedCount.valueOr(0) + listTags( config ); if( config.listReporters() ) listedCount = listedCount.valueOr(0) + listReporters( config ); return listedCount; } } // end namespace Catch // end catch_list.cpp // start catch_matchers.cpp namespace Catch { namespace Matchers { namespace Impl { std::string MatcherUntypedBase::toString() const { if( m_cachedToString.empty() ) m_cachedToString = describe(); return m_cachedToString; } MatcherUntypedBase::~MatcherUntypedBase() = default; } // namespace Impl } // namespace Matchers using namespace Matchers; using Matchers::Impl::MatcherBase; } // namespace Catch // end catch_matchers.cpp // start catch_matchers_string.cpp namespace Catch { namespace Matchers { namespace StdString { CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_str( adjustString( str ) ) {} std::string CasedString::adjustString( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } std::string CasedString::caseSensitivitySuffix() const { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" : std::string(); } StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) : m_comparator( comparator ), m_operation( operation ) { } std::string StringMatcherBase::describe() const { std::string description; description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + m_comparator.caseSensitivitySuffix().size()); description += m_operation; description += ": \""; description += m_comparator.m_str; description += "\""; description += m_comparator.caseSensitivitySuffix(); return description; } EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} bool EqualsMatcher::match( std::string const& source ) const { return m_comparator.adjustString( source ) == m_comparator.m_str; } ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} bool ContainsMatcher::match( std::string const& source ) const { return contains( m_comparator.adjustString( source ), m_comparator.m_str ); } StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} bool StartsWithMatcher::match( std::string const& source ) const { return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); } EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} bool EndsWithMatcher::match( std::string const& source ) const { return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); } } // namespace StdString StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); } StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); } } // namespace Matchers } // namespace Catch // end catch_matchers_string.cpp // start catch_message.cpp namespace Catch { MessageInfo::MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), sequence( ++globalCount ) {} bool MessageInfo::operator==( MessageInfo const& other ) const { return sequence == other.sequence; } bool MessageInfo::operator<( MessageInfo const& other ) const { return sequence < other.sequence; } // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// Catch::MessageBuilder::MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ) :m_info(macroName, lineInfo, type) {} //////////////////////////////////////////////////////////////////////////// ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } ScopedMessage::~ScopedMessage() { if ( !std::uncaught_exception() ){ getResultCapture().popScopedMessage(m_info); } } } // end namespace Catch // end catch_message.cpp // start catch_random_number_generator.cpp // start catch_random_number_generator.h #include namespace Catch { struct IConfig; void seedRng( IConfig const& config ); unsigned int rngSeed(); struct RandomNumberGenerator { using result_type = unsigned int; static constexpr result_type (min)() { return 0; } static constexpr result_type (max)() { return 1000000; } result_type operator()( result_type n ) const; result_type operator()() const; template static void shuffle( V& vector ) { RandomNumberGenerator rng; std::shuffle( vector.begin(), vector.end(), rng ); } }; } // end catch_random_number_generator.h #include namespace Catch { void seedRng( IConfig const& config ) { if( config.rngSeed() != 0 ) std::srand( config.rngSeed() ); } unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { return std::rand() % n; } RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { return std::rand() % (max)(); } } // end catch_random_number_generator.cpp // start catch_registry_hub.cpp // start catch_test_case_registry_impl.h #include #include #include #include namespace Catch { class TestCase; struct IConfig; std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); void enforceNoDuplicateTestCases( std::vector const& functions ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); class TestRegistry : public ITestCaseRegistry { public: virtual ~TestRegistry() = default; virtual void registerTest( TestCase const& testCase ); std::vector const& getAllTests() const override; std::vector const& getAllTestsSorted( IConfig const& config ) const override; private: std::vector m_functions; mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; mutable std::vector m_sortedFunctions; std::size_t m_unnamedCount = 0; std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; /////////////////////////////////////////////////////////////////////////// class TestInvokerAsFunction : public ITestInvoker { void(*m_testAsFunction)(); public: TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; void invoke() const override; }; std::string extractClassName( std::string const& classOrQualifiedMethodName ); /////////////////////////////////////////////////////////////////////////// } // end namespace Catch // end catch_test_case_registry_impl.h // start catch_reporter_registry.h #include namespace Catch { class ReporterRegistry : public IReporterRegistry { public: ~ReporterRegistry() override; IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); void registerListener( IReporterFactoryPtr const& factory ); FactoryMap const& getFactories() const override; Listeners const& getListeners() const override; private: FactoryMap m_factories; Listeners m_listeners; }; } // end catch_reporter_registry.h // start catch_tag_alias_registry.h // start catch_tag_alias.h #include namespace Catch { struct TagAlias { TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); std::string tag; SourceLineInfo lineInfo; }; } // end namespace Catch // end catch_tag_alias.h #include namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: ~TagAliasRegistry() override; TagAlias const* find( std::string const& alias ) const override; std::string expandAliases( std::string const& unexpandedTestSpec ) const override; void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); private: std::map m_registry; }; } // end namespace Catch // end catch_tag_alias_registry.h // start catch_startup_exception_registry.h #include #include namespace Catch { class StartupExceptionRegistry { public: void add(std::exception_ptr const& exception) noexcept; std::vector const& getExceptions() const noexcept; private: std::vector m_exceptions; }; } // end namespace Catch // end catch_startup_exception_registry.h namespace Catch { namespace { class RegistryHub : public IRegistryHub, public IMutableRegistryHub, private NonCopyable { public: // IRegistryHub RegistryHub() = default; IReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { return m_testCaseRegistry; } IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { return m_exceptionTranslatorRegistry; } ITagAliasRegistry const& getTagAliasRegistry() const override { return m_tagAliasRegistry; } StartupExceptionRegistry const& getStartupExceptionRegistry() const override { return m_exceptionRegistry; } public: // IMutableRegistryHub void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { m_reporterRegistry.registerReporter( name, factory ); } void registerListener( IReporterFactoryPtr const& factory ) override { m_reporterRegistry.registerListener( factory ); } void registerTest( TestCase const& testInfo ) override { m_testCaseRegistry.registerTest( testInfo ); } void registerTranslator( const IExceptionTranslator* translator ) override { m_exceptionTranslatorRegistry.registerTranslator( translator ); } void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { m_tagAliasRegistry.add( alias, tag, lineInfo ); } void registerStartupException() noexcept override { m_exceptionRegistry.add(std::current_exception()); } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; StartupExceptionRegistry m_exceptionRegistry; }; // Single, global, instance RegistryHub*& getTheRegistryHub() { static RegistryHub* theRegistryHub = nullptr; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; } } IRegistryHub& getRegistryHub() { return *getTheRegistryHub(); } IMutableRegistryHub& getMutableRegistryHub() { return *getTheRegistryHub(); } void cleanUp() { delete getTheRegistryHub(); getTheRegistryHub() = nullptr; cleanUpContext(); } std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } } // end namespace Catch // end catch_registry_hub.cpp // start catch_reporter_registry.cpp namespace Catch { ReporterRegistry::~ReporterRegistry() = default; IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { auto it = m_factories.find( name ); if( it == m_factories.end() ) return nullptr; return it->second->create( ReporterConfig( config ) ); } void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { m_factories.emplace(name, factory); } void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { m_listeners.push_back( factory ); } IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { return m_factories; } IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { return m_listeners; } } // end catch_reporter_registry.cpp // start catch_result_type.cpp namespace Catch { bool isOk( ResultWas::OfType resultType ) { return ( resultType & ResultWas::FailureBit ) == 0; } bool isJustInfo( int flags ) { return flags == ResultWas::Info; } ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { return static_cast( static_cast( lhs ) | static_cast( rhs ) ); } bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch // end catch_result_type.cpp // start catch_run_context.cpp // start catch_run_context.h #include namespace Catch { struct IMutableContext; class StreamRedirect { public: StreamRedirect(std::ostream& stream, std::string& targetString); ~StreamRedirect(); private: std::ostream& m_stream; std::streambuf* m_prevBuf; std::ostringstream m_oss; std::string& m_targetString; }; // StdErr has two constituent streams in C++, std::cerr and std::clog // This means that we need to redirect 2 streams into 1 to keep proper // order of writes and cannot use StreamRedirect on its own class StdErrRedirect { public: StdErrRedirect(std::string& targetString); ~StdErrRedirect(); private: std::streambuf* m_cerrBuf; std::streambuf* m_clogBuf; std::ostringstream m_oss; std::string& m_targetString; }; /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { public: RunContext( RunContext const& ) = delete; RunContext& operator =( RunContext const& ) = delete; explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); virtual ~RunContext(); void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount); void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount); Totals runTest(TestCase const& testCase); IConfigPtr config() const; IStreamingReporter& reporter() const; private: // IResultCapture void assertionStarting(AssertionInfo const& info) override; void assertionEnded(AssertionResult const& result) override; bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; bool testForMissingAssertions(Counts& assertions); void sectionEnded(SectionEndInfo const& endInfo) override; void sectionEndedEarly(SectionEndInfo const& endInfo) override; void benchmarkStarting( BenchmarkInfo const& info ) override; void benchmarkEnded( BenchmarkStats const& stats ) override; void pushScopedMessage(MessageInfo const& message) override; void popScopedMessage(MessageInfo const& message) override; std::string getCurrentTestName() const override; const AssertionResult* getLastResult() const override; void exceptionEarlyReported() override; void handleFatalErrorCondition( StringRef message ) override; bool lastAssertionPassed() override; void assertionPassed() override; void assertionRun() override; public: // !TBD We need to do this another way! bool aborting() const override; private: void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr); void invokeActiveTestCase(); private: void handleUnfinishedSections(); TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker; Option m_lastResult; IConfigPtr m_config; Totals m_totals; IStreamingReporterPtr m_reporter; std::vector m_messages; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; std::size_t m_prevPassed = 0; bool m_shouldReportUnexpected = true; }; IResultCapture& getResultCapture(); } // end namespace Catch // end catch_run_context.h #include #include namespace Catch { StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) : m_stream(stream), m_prevBuf(stream.rdbuf()), m_targetString(targetString) { stream.rdbuf(m_oss.rdbuf()); } StreamRedirect::~StreamRedirect() { m_targetString += m_oss.str(); m_stream.rdbuf(m_prevBuf); } StdErrRedirect::StdErrRedirect(std::string & targetString) :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), m_targetString(targetString) { cerr().rdbuf(m_oss.rdbuf()); clog().rdbuf(m_oss.rdbuf()); } StdErrRedirect::~StdErrRedirect() { m_targetString += m_oss.str(); cerr().rdbuf(m_cerrBuf); clog().rdbuf(m_clogBuf); } RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) : m_runInfo(_config->name()), m_context(getCurrentMutableContext()), m_config(_config), m_reporter(std::move(reporter)), m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal } { m_context.setRunner(this); m_context.setConfig(m_config); m_context.setResultCapture(this); m_reporter->testRunStarting(m_runInfo); } RunContext::~RunContext() { m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); } void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); } void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); } Totals RunContext::runTest(TestCase const& testCase) { Totals prevTotals = m_totals; std::string redirectedCout; std::string redirectedCerr; TestCaseInfo testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting(testInfo); m_activeTestCase = &testCase; ITracker& rootTracker = m_trackerContext.startRun(); assert(rootTracker.isSectionTracker()); static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); runCurrentTest(redirectedCout, redirectedCerr); } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); Totals deltaTotals = m_totals.delta(prevTotals); if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { deltaTotals.assertions.failed++; deltaTotals.testCases.passed--; deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, redirectedCout, redirectedCerr, aborting())); m_activeTestCase = nullptr; m_testCaseTracker = nullptr; return deltaTotals; } IConfigPtr RunContext::config() const { return m_config; } IStreamingReporter& RunContext::reporter() const { return *m_reporter; } void RunContext::assertionStarting(AssertionInfo const& info) { m_reporter->assertionStarting( info ); } void RunContext::assertionEnded(AssertionResult const & result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; } else if (!result.isOk()) { if( m_activeTestCase->getTestCaseInfo().okToFail() ) m_totals.assertions.failedButOk++; else m_totals.assertions.failed++; } // We have no use for the return value (whether messages should be cleared), because messages were made scoped // and should be let to clear themselves out. static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition }; m_lastResult = result; } bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); if (!sectionTracker.isOpen()) return false; m_activeSections.push_back(§ionTracker); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_reporter->sectionStarting(sectionInfo); assertions = m_totals.assertions; return true; } bool RunContext::testForMissingAssertions(Counts& assertions) { if (assertions.total() != 0) return false; if (!m_config->warnAboutMissingAssertions()) return false; if (m_trackerContext.currentTracker().hasChildren()) return false; m_totals.assertions.failed++; assertions.failed++; return true; } void RunContext::sectionEnded(SectionEndInfo const & endInfo) { Counts assertions = m_totals.assertions - endInfo.prevAssertions; bool missingAssertions = testForMissingAssertions(assertions); if (!m_activeSections.empty()) { m_activeSections.back()->close(); m_activeSections.pop_back(); } m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); m_messages.clear(); } void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { if (m_unfinishedSections.empty()) m_activeSections.back()->fail(); else m_activeSections.back()->close(); m_activeSections.pop_back(); m_unfinishedSections.push_back(endInfo); } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { m_reporter->benchmarkStarting( info ); } void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { m_reporter->benchmarkEnded( stats ); } void RunContext::pushScopedMessage(MessageInfo const & message) { m_messages.push_back(message); } void RunContext::popScopedMessage(MessageInfo const & message) { m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); } std::string RunContext::getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name : std::string(); } const AssertionResult * RunContext::getLastResult() const { return &(*m_lastResult); } void RunContext::exceptionEarlyReported() { m_shouldReportUnexpected = false; } void RunContext::handleFatalErrorCondition( StringRef message ) { // First notify reporter that bad things happened m_reporter->fatalErrorEncountered(message); // Don't rebuild the result -- the stringification itself can cause more fatal errors // Instead, fake a result data. AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); tempResult.message = message; AssertionResult result(m_lastAssertionInfo, tempResult); getResultCapture().assertionEnded(result); handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); m_reporter->sectionEnded(testCaseSectionStats); auto const& testInfo = m_activeTestCase->getTestCaseInfo(); Totals deltaTotals; deltaTotals.testCases.failed = 1; deltaTotals.assertions.failed = 1; m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, std::string(), std::string(), false)); m_totals.testCases.failed++; testGroupEnded(std::string(), m_totals, 1, 1); m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); } bool RunContext::lastAssertionPassed() { return m_totals.assertions.passed == (m_prevPassed + 1); } void RunContext::assertionPassed() { ++m_totals.assertions.passed; m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; m_lastAssertionInfo.macroName = ""; } void RunContext::assertionRun() { m_prevPassed = m_totals.assertions.passed; } bool RunContext::aborting() const { return m_totals.assertions.failed == static_cast(m_config->abortAfter()); } void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); m_reporter->sectionStarting(testCaseSection); Counts prevAssertions = m_totals.assertions; double duration = 0; m_shouldReportUnexpected = true; try { m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; seedRng(*m_config); Timer timer; timer.start(); if (m_reporter->getPreferences().shouldRedirectStdOut) { StreamRedirect coutRedir(cout(), redirectedCout); StdErrRedirect errRedir(redirectedCerr); invokeActiveTestCase(); } else { invokeActiveTestCase(); } duration = timer.getElapsedSeconds(); } catch (TestFailureException&) { // This just means the test was aborted due to failure } catch (...) { // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions // are reported without translation at the point of origin. if (m_shouldReportUnexpected) { AssertionHandler ( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ).useActiveException(); } } m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions(assertions); SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); } void RunContext::invokeActiveTestCase() { FatalConditionHandler fatalConditionHandler; // Handle signals m_activeTestCase->invoke(); fatalConditionHandler.reset(); } void RunContext::handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. for (auto it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it) sectionEnded(*it); m_unfinishedSections.clear(); } IResultCapture& getResultCapture() { if (auto* capture = getCurrentContext().getResultCapture()) return *capture; else CATCH_INTERNAL_ERROR("No result capture instance"); } } // end catch_run_context.cpp // start catch_section.cpp namespace Catch { Section::Section( SectionInfo const& info ) : m_info( info ), m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) { m_timer.start(); } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 #endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); if( std::uncaught_exception() ) getResultCapture().sectionEndedEarly( endInfo ); else getResultCapture().sectionEnded( endInfo ); } } #if defined(_MSC_VER) #pragma warning(pop) #endif // This indicates whether the section should be executed or not Section::operator bool() const { return m_sectionIncluded; } } // end namespace Catch // end catch_section.cpp // start catch_section_info.cpp namespace Catch { SectionInfo::SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description ) : name( _name ), description( _description ), lineInfo( _lineInfo ) {} SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) {} } // end namespace Catch // end catch_section_info.cpp // start catch_session.cpp // start catch_session.h #include namespace Catch { class Session : NonCopyable { public: Session(); ~Session() override; void showHelp() const; void libIdentify(); int applyCommandLine( int argc, char* argv[] ); void useConfigData( ConfigData const& configData ); int run( int argc, char* argv[] ); #if defined(WIN32) && defined(UNICODE) int run( int argc, wchar_t* const argv[] ); #endif int run(); clara::Parser const& cli() const; void cli( clara::Parser const& newParser ); ConfigData& configData(); Config& config(); private: int runInternal(); clara::Parser m_cli; ConfigData m_configData; std::shared_ptr m_config; bool m_startupExceptions = false; }; } // end namespace Catch // end catch_session.h // start catch_version.h #include namespace Catch { // Versioning information struct Version { Version( Version const& ) = delete; Version& operator=( Version const& ) = delete; Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; unsigned int const minorVersion; unsigned int const patchNumber; // buildNumber is only used if branchName is not null char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); }; Version const& libraryVersion(); } // end catch_version.h #include #include namespace { const int MaxExitCode = 255; using Catch::IStreamingReporterPtr; using Catch::IConfigPtr; using Catch::Config; IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); return reporter; } #ifndef CATCH_CONFIG_DEFAULT_REPORTER #define CATCH_CONFIG_DEFAULT_REPORTER "console" #endif IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { auto const& reporterNames = config->getReporterNames(); if (reporterNames.empty()) return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); IStreamingReporterPtr reporter; for (auto const& name : reporterNames) addReporter(reporter, createReporter(name, config)); return reporter; } #undef CATCH_CONFIG_DEFAULT_REPORTER void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); for (auto const& listener : listeners) addReporter(reporters, listener->create(Catch::ReporterConfig(config))); } Catch::Totals runTests(std::shared_ptr const& config) { using namespace Catch; IStreamingReporterPtr reporter = makeReporter(config); addListeners(reporter, config); RunContext context(config, std::move(reporter)); Totals totals; context.testGroupStarting(config->name(), 1, 1); TestSpec testSpec = config->testSpec(); if (!testSpec.hasFilters()) testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests auto const& allTestCases = getAllTestCasesSorted(*config); for (auto const& testCase : allTestCases) { if (!context.aborting() && matchTest(testCase, testSpec, *config)) totals += context.runTest(testCase); else context.reporter().skipTest(testCase); } context.testGroupEnded(config->name(), totals, 1, 1); return totals; } void applyFilenamesAsTags(Catch::IConfig const& config) { using namespace Catch; auto& tests = const_cast&>(getAllTestCasesSorted(config)); for (auto& testCase : tests) { auto tags = testCase.tags; std::string filename = testCase.lineInfo.file; auto lastSlash = filename.find_last_of("\\/"); if (lastSlash != std::string::npos) { filename.erase(0, lastSlash); filename[0] = '#'; } auto lastDot = filename.find_last_of('.'); if (lastDot != std::string::npos) { filename.erase(lastDot); } tags.push_back(std::move(filename)); setTags(testCase, tags); } } } namespace Catch { Session::Session() { static bool alreadyInstantiated = false; if( alreadyInstantiated ) { try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } catch(...) { getMutableRegistryHub().registerStartupException(); } } const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); if ( !exceptions.empty() ) { m_startupExceptions = true; Colour colourGuard( Colour::Red ); Catch::cerr() << "Errors occured during startup!" << '\n'; // iterate over all exceptions and notify user for ( const auto& ex_ptr : exceptions ) { try { std::rethrow_exception(ex_ptr); } catch ( std::exception const& ex ) { Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; } } } alreadyInstantiated = true; m_cli = makeCommandLineParser( m_configData ); } Session::~Session() { Catch::cleanUp(); } void Session::showHelp() const { Catch::cout() << "\nCatch v" << libraryVersion() << "\n" << m_cli << std::endl << "For more detailed usage please see the project docs\n" << std::endl; } void Session::libIdentify() { Catch::cout() << std::left << std::setw(16) << "description: " << "A Catch test executable\n" << std::left << std::setw(16) << "category: " << "testframework\n" << std::left << std::setw(16) << "framework: " << "Catch Test\n" << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; } int Session::applyCommandLine( int argc, char* argv[] ) { if( m_startupExceptions ) return 1; auto result = m_cli.parse( clara::Args( argc, argv ) ); if( !result ) { Catch::cerr() << Colour( Colour::Red ) << "\nError(s) in input:\n" << Column( result.errorMessage() ).indent( 2 ) << "\n\n"; Catch::cerr() << "Run with -? for usage\n" << std::endl; return MaxExitCode; } if( m_configData.showHelp ) showHelp(); if( m_configData.libIdentify ) libIdentify(); m_config.reset(); return 0; } void Session::useConfigData( ConfigData const& configData ) { m_configData = configData; m_config.reset(); } int Session::run( int argc, char* argv[] ) { if( m_startupExceptions ) return 1; int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) returnCode = run(); return returnCode; } #if defined(WIN32) && defined(UNICODE) int Session::run( int argc, wchar_t* const argv[] ) { char **utf8Argv = new char *[ argc ]; for ( int i = 0; i < argc; ++i ) { int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); utf8Argv[ i ] = new char[ bufSize ]; WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); } int returnCode = run( argc, utf8Argv ); for ( int i = 0; i < argc; ++i ) delete [] utf8Argv[ i ]; delete [] utf8Argv; return returnCode; } #endif int Session::run() { if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { Catch::cout() << "...waiting for enter/ return before starting" << std::endl; static_cast(std::getchar()); } int exitCode = runInternal(); if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; static_cast(std::getchar()); } return exitCode; } clara::Parser const& Session::cli() const { return m_cli; } void Session::cli( clara::Parser const& newParser ) { m_cli = newParser; } ConfigData& Session::configData() { return m_configData; } Config& Session::config() { if( !m_config ) m_config = std::make_shared( m_configData ); return *m_config; } int Session::runInternal() { if( m_startupExceptions ) return 1; if( m_configData.showHelp || m_configData.libIdentify ) return 0; try { config(); // Force config to be constructed seedRng( *m_config ); if( m_configData.filenamesAsTags ) applyFilenamesAsTags( *m_config ); // Handle list request if( Option listed = list( config() ) ) return static_cast( *listed ); return (std::min)( MaxExitCode, static_cast( runTests( m_config ).assertions.failed ) ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; return MaxExitCode; } } } // end namespace Catch // end catch_session.cpp // start catch_startup_exception_registry.cpp namespace Catch { void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { try { m_exceptions.push_back(exception); } catch(...) { // If we run out of memory during start-up there's really not a lot more we can do about it std::terminate(); } } std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { return m_exceptions; } } // end namespace Catch // end catch_startup_exception_registry.cpp // start catch_stream.cpp #include #include #include namespace Catch { template class StreamBufImpl : public StreamBufBase { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() noexcept { StreamBufImpl::sync(); } private: int overflow( int c ) override { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() override { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); } return 0; } }; /////////////////////////////////////////////////////////////////////////// Catch::IStream::~IStream() = default; FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); } std::ostream& FileStream::stream() const { return m_ofs; } struct OutputDebugWriter { void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; DebugOutStream::DebugOutStream() : m_streamBuf( new StreamBufImpl() ), m_os( m_streamBuf.get() ) {} std::ostream& DebugOutStream::stream() const { return m_os; } // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream::CoutStream() : m_os( Catch::cout().rdbuf() ) {} std::ostream& CoutStream::stream() const { return m_os; } #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions std::ostream& cout() { return std::cout; } std::ostream& cerr() { return std::cerr; } std::ostream& clog() { return std::clog; } #endif } // end catch_stream.cpp // start catch_streambuf.cpp namespace Catch { StreamBufBase::~StreamBufBase() = default; } // end catch_streambuf.cpp // start catch_string_manip.cpp #include #include #include #include namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } bool startsWith( std::string const& s, char prefix ) { return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); } bool endsWith( std::string const& s, char suffix ) { return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } char toLowerCh(char c) { return static_cast( std::tolower( c ) ); } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; toLowerInPlace( lc ); return lc; } std::string trim( std::string const& str ) { static char const* whitespaceChars = "\n\r\t "; std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { bool replaced = false; std::size_t i = str.find( replaceThis ); while( i != std::string::npos ) { replaced = true; str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); if( i < str.size()-withThis.size() ) i = str.find( replaceThis, i+withThis.size() ); else i = std::string::npos; } return replaced; } pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) os << 's'; return os; } } // end catch_string_manip.cpp // start catch_stringref.cpp #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif #include #include #include namespace Catch { auto getEmptyStringRef() -> StringRef { static StringRef s_emptyStringRef(""); return s_emptyStringRef; } StringRef::StringRef() noexcept : StringRef( getEmptyStringRef() ) {} StringRef::StringRef( StringRef const& other ) noexcept : m_start( other.m_start ), m_size( other.m_size ) {} StringRef::StringRef( StringRef&& other ) noexcept : m_start( other.m_start ), m_size( other.m_size ), m_data( other.m_data ) { other.m_data = nullptr; } StringRef::StringRef( char const* rawChars ) noexcept : m_start( rawChars ), m_size( static_cast( std::strlen( rawChars ) ) ) { assert( rawChars != nullptr ); } StringRef::StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) { size_type rawSize = rawChars == nullptr ? 0 : static_cast( std::strlen( rawChars ) ); if( rawSize < size ) m_size = rawSize; } StringRef::StringRef( std::string const& stdString ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} StringRef::~StringRef() noexcept { delete[] m_data; } auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& { swap( other ); return *this; } StringRef::operator std::string() const { return std::string( m_start, m_size ); } void StringRef::swap( StringRef& other ) noexcept { std::swap( m_start, other.m_start ); std::swap( m_size, other.m_size ); std::swap( m_data, other.m_data ); } auto StringRef::c_str() const -> char const* { if( isSubstring() ) const_cast( this )->takeOwnership(); return m_start; } auto StringRef::data() const noexcept -> char const* { return m_start; } auto StringRef::isOwned() const noexcept -> bool { return m_data != nullptr; } auto StringRef::isSubstring() const noexcept -> bool { return m_start[m_size] != '\0'; } void StringRef::takeOwnership() { if( !isOwned() ) { m_data = new char[m_size+1]; memcpy( m_data, m_start, m_size ); m_data[m_size] = '\0'; m_start = m_data; } } auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { if( start < m_size ) return StringRef( m_start+start, size ); else return StringRef(); } auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { return size() == other.size() && (std::strncmp( m_start, other.m_start, size() ) == 0); } auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { return !operator==( other ); } auto StringRef::operator[](size_type index) const noexcept -> char { return m_start[index]; } auto StringRef::empty() const noexcept -> bool { return m_size == 0; } auto StringRef::size() const noexcept -> size_type { return m_size; } auto StringRef::numberOfCharacters() const noexcept -> size_type { size_type noChars = m_size; // Make adjustments for uft encodings for( size_type i=0; i < m_size; ++i ) { char c = m_start[i]; if( ( c & 0b11000000 ) == 0b11000000 ) { if( ( c & 0b11100000 ) == 0b11000000 ) noChars--; else if( ( c & 0b11110000 ) == 0b11100000 ) noChars-=2; else if( ( c & 0b11111000 ) == 0b11110000 ) noChars-=3; } } return noChars; } auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { std::string str; str.reserve( lhs.size() + rhs.size() ); str += lhs; str += rhs; return str; } auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { return std::string( lhs ) + std::string( rhs ); } auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { return std::string( lhs ) + std::string( rhs ); } auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { return os << str.c_str(); } } // namespace Catch #if defined(__clang__) # pragma clang diagnostic pop #endif // end catch_stringref.cpp // start catch_tag_alias.cpp namespace Catch { TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} } // end catch_tag_alias.cpp // start catch_tag_alias_autoregistrar.cpp namespace Catch { RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { try { getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); } catch (...) { // Do not throw when constructing global objects, instead register the exception to be processed later getMutableRegistryHub().registerStartupException(); } } } // end catch_tag_alias_autoregistrar.cpp // start catch_tag_alias_registry.cpp namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); if( it != m_registry.end() ) return &(it->second); else return nullptr; } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string expandedTestSpec = unexpandedTestSpec; for( auto const& registryKvp : m_registry ) { std::size_t pos = expandedTestSpec.find( registryKvp.first ); if( pos != std::string::npos ) { expandedTestSpec = expandedTestSpec.substr( 0, pos ) + registryKvp.second.tag + expandedTestSpec.substr( pos + registryKvp.first.size() ); } } return expandedTestSpec; } void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, "error: tag alias, '" << alias << "' already registered.\n" << "\tFirst seen at: " << find(alias)->lineInfo << "\n" << "\tRedefined at: " << lineInfo ); } ITagAliasRegistry::~ITagAliasRegistry() {} ITagAliasRegistry const& ITagAliasRegistry::get() { return getRegistryHub().getTagAliasRegistry(); } } // end namespace Catch // end catch_tag_alias_registry.cpp // start catch_test_case_info.cpp #include #include #include namespace Catch { TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { if( startsWith( tag, '.' ) || tag == "!hide" ) return TestCaseInfo::IsHidden; else if( tag == "!throws" ) return TestCaseInfo::Throws; else if( tag == "!shouldfail" ) return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; else if( tag == "!nonportable" ) return TestCaseInfo::NonPortable; else if( tag == "!benchmark" ) return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); else return TestCaseInfo::None; } bool isReservedTag( std::string const& tag ) { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { CATCH_ENFORCE( !isReservedTag(tag), "Tag name: [" << tag << "] is not allowed.\n" << "Tag names starting with non alpha-numeric characters are reserved\n" << _lineInfo ); } TestCase makeTestCase( ITestInvoker* _testCase, std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) { bool isHidden = false; // Parse out tags std::vector tags; std::string desc, tag; bool inTag = false; for (char c : _descOrTags) { if( !inTag ) { if( c == '[' ) inTag = true; else desc += c; } else { if( c == ']' ) { TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); if( ( prop & TestCaseInfo::IsHidden ) != 0 ) isHidden = true; else if( prop == TestCaseInfo::None ) enforceNotReservedTag( tag, _lineInfo ); tags.push_back( tag ); tag.clear(); inTag = false; } else tag += c; } } if( isHidden ) { tags.push_back( "." ); } TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { std::sort(begin(tags), end(tags)); tags.erase(std::unique(begin(tags), end(tags)), end(tags)); testCaseInfo.lcaseTags.clear(); for( auto const& tag : tags ) { std::string lcaseTag = toLower( tag ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.push_back( lcaseTag ); } testCaseInfo.tags = std::move(tags); } TestCaseInfo::TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::vector const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) { setTags( *this, _tags ); } bool TestCaseInfo::isHidden() const { return ( properties & IsHidden ) != 0; } bool TestCaseInfo::throws() const { return ( properties & Throws ) != 0; } bool TestCaseInfo::okToFail() const { return ( properties & (ShouldFail | MayFail ) ) != 0; } bool TestCaseInfo::expectedToFail() const { return ( properties & (ShouldFail ) ) != 0; } std::string TestCaseInfo::tagsAsString() const { std::string ret; // '[' and ']' per tag std::size_t full_size = 2 * tags.size(); for (const auto& tag : tags) { full_size += tag.size(); } ret.reserve(full_size); for (const auto& tag : tags) { ret.push_back('['); ret.append(tag); ret.push_back(']'); } return ret; } TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); other.name = _newName; return other; } void TestCase::invoke() const { test->invoke(); } bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { return name < other.name; } TestCaseInfo const& TestCase::getTestCaseInfo() const { return *this; } } // end namespace Catch // end catch_test_case_info.cpp // start catch_test_case_registry_impl.cpp #include namespace Catch { std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { std::vector sorted = unsortedTestCases; switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: std::sort( sorted.begin(), sorted.end() ); break; case RunTests::InRandomOrder: seedRng( config ); RandomNumberGenerator::shuffle( sorted ); break; case RunTests::InDeclarationOrder: // already in declaration order break; } return sorted; } bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); } void enforceNoDuplicateTestCases( std::vector const& functions ) { std::set seenFunctions; for( auto const& function : functions ) { auto prev = seenFunctions.insert( function ); CATCH_ENFORCE( prev.second, "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); } } std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); for( auto const& testCase : testCases ) if( matchTest( testCase, testSpec, config ) ) filtered.push_back( testCase ); return filtered; } std::vector const& getAllTestCasesSorted( IConfig const& config ) { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } void TestRegistry::registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); } m_functions.push_back( testCase ); } std::vector const& TestRegistry::getAllTests() const { return m_functions; } std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { if( m_sortedFunctions.empty() ) enforceNoDuplicateTestCases( m_functions ); if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { m_sortedFunctions = sortTests( config, m_functions ); m_currentSortOrder = config.runOrder(); } return m_sortedFunctions; } /////////////////////////////////////////////////////////////////////////// TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} void TestInvokerAsFunction::invoke() const { m_testAsFunction(); } std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; className = className.substr( penultimateColons, lastColons-penultimateColons ); } return className; } } // end namespace Catch // end catch_test_case_registry_impl.cpp // start catch_test_case_tracker.cpp #include #include #include #include #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif namespace Catch { namespace TestCaseTracking { NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) : name( _name ), location( _location ) {} ITracker::~ITracker() = default; TrackerContext& TrackerContext::instance() { static TrackerContext s_instance; return s_instance; } ITracker& TrackerContext::startRun() { m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); m_currentTracker = nullptr; m_runState = Executing; return *m_rootTracker; } void TrackerContext::endRun() { m_rootTracker.reset(); m_currentTracker = nullptr; m_runState = NotStarted; } void TrackerContext::startCycle() { m_currentTracker = m_rootTracker.get(); m_runState = Executing; } void TrackerContext::completeCycle() { m_runState = CompletedCycle; } bool TrackerContext::completedCycle() const { return m_runState == CompletedCycle; } ITracker& TrackerContext::currentTracker() { return *m_currentTracker; } void TrackerContext::setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { return tracker->nameAndLocation().name == m_nameAndLocation.name && tracker->nameAndLocation().location == m_nameAndLocation.location; } TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ) {} NameAndLocation const& TrackerBase::nameAndLocation() const { return m_nameAndLocation; } bool TrackerBase::isComplete() const { return m_runState == CompletedSuccessfully || m_runState == Failed; } bool TrackerBase::isSuccessfullyCompleted() const { return m_runState == CompletedSuccessfully; } bool TrackerBase::isOpen() const { return m_runState != NotStarted && !isComplete(); } bool TrackerBase::hasChildren() const { return !m_children.empty(); } void TrackerBase::addChild( ITrackerPtr const& child ) { m_children.push_back( child ); } ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? *it : nullptr; } ITracker& TrackerBase::parent() { assert( m_parent ); // Should always be non-null except for root return *m_parent; } void TrackerBase::openChild() { if( m_runState != ExecutingChildren ) { m_runState = ExecutingChildren; if( m_parent ) m_parent->openChild(); } } bool TrackerBase::isSectionTracker() const { return false; } bool TrackerBase::isIndexTracker() const { return false; } void TrackerBase::open() { m_runState = Executing; moveToThis(); if( m_parent ) m_parent->openChild(); } void TrackerBase::close() { // Close any still open children (e.g. generators) while( &m_ctx.currentTracker() != this ) m_ctx.currentTracker().close(); switch( m_runState ) { case NeedsAnotherRun: break; case Executing: m_runState = CompletedSuccessfully; break; case ExecutingChildren: if( m_children.empty() || m_children.back()->isComplete() ) m_runState = CompletedSuccessfully; break; case NotStarted: case CompletedSuccessfully: case Failed: CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); default: CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); } moveToParent(); m_ctx.completeCycle(); } void TrackerBase::fail() { m_runState = Failed; if( m_parent ) m_parent->markAsNeedingAnotherRun(); moveToParent(); m_ctx.completeCycle(); } void TrackerBase::markAsNeedingAnotherRun() { m_runState = NeedsAnotherRun; } void TrackerBase::moveToParent() { assert( m_parent ); m_ctx.setCurrentTracker( m_parent ); } void TrackerBase::moveToThis() { m_ctx.setCurrentTracker( this ); } SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( nameAndLocation, ctx, parent ) { if( parent ) { while( !parent->isSectionTracker() ) parent = &parent->parent(); SectionTracker& parentSection = static_cast( *parent ); addNextFilters( parentSection.m_filters ); } } bool SectionTracker::isSectionTracker() const { return true; } SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { std::shared_ptr section; ITracker& currentTracker = ctx.currentTracker(); if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = std::static_pointer_cast( childTracker ); } else { section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } if( !ctx.completedCycle() ) section->tryOpen(); return *section; } void SectionTracker::tryOpen() { if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) open(); } void SectionTracker::addInitialFilters( std::vector const& filters ) { if( !filters.empty() ) { m_filters.push_back(""); // Root - should never be consulted m_filters.push_back(""); // Test Case - not a section filter m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } } void SectionTracker::addNextFilters( std::vector const& filters ) { if( filters.size() > 1 ) m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); } IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ) {} bool IndexTracker::isIndexTracker() const { return true; } IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { std::shared_ptr tracker; ITracker& currentTracker = ctx.currentTracker(); if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = std::static_pointer_cast( childTracker ); } else { tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } if( !ctx.completedCycle() && !tracker->isComplete() ) { if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) tracker->moveNext(); tracker->open(); } return *tracker; } int IndexTracker::index() const { return m_index; } void IndexTracker::moveNext() { m_index++; m_children.clear(); } void IndexTracker::close() { TrackerBase::close(); if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) m_runState = Executing; } } // namespace TestCaseTracking using TestCaseTracking::ITracker; using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; using TestCaseTracking::IndexTracker; } // namespace Catch #if defined(__clang__) # pragma clang diagnostic pop #endif // end catch_test_case_tracker.cpp // start catch_test_registry.cpp namespace Catch { auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); } NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { try { getMutableRegistryHub() .registerTest( makeTestCase( invoker, extractClassName( classOrMethod ), nameAndTags.name, nameAndTags.tags, lineInfo)); } catch (...) { // Do not throw when constructing global objects, instead register the exception to be processed later getMutableRegistryHub().registerStartupException(); } } AutoReg::~AutoReg() = default; } // end catch_test_registry.cpp // start catch_test_spec.cpp #include #include #include #include namespace Catch { TestSpec::Pattern::~Pattern() = default; TestSpec::NamePattern::~NamePattern() = default; TestSpec::TagPattern::~TagPattern() = default; TestSpec::ExcludedPattern::~ExcludedPattern() = default; TestSpec::NamePattern::NamePattern( std::string const& name ) : m_wildcardPattern( toLower( name ), CaseSensitive::No ) {} bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { return m_wildcardPattern.matches( toLower( testCase.name ) ); } TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { return std::find(begin(testCase.lcaseTags), end(testCase.lcaseTags), m_tag) != end(testCase.lcaseTags); } TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match for( auto const& pattern : m_patterns ) { if( !pattern->matches( testCase ) ) return false; } return true; } bool TestSpec::hasFilters() const { return !m_filters.empty(); } bool TestSpec::matches( TestCaseInfo const& testCase ) const { // A TestSpec matches if any filter matches for( auto const& filter : m_filters ) if( filter.matches( testCase ) ) return true; return false; } } // end catch_test_spec.cpp // start catch_test_spec_parser.cpp namespace Catch { TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser& TestSpecParser::parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); m_escapeChars.clear(); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) addPattern(); return *this; } TestSpec TestSpecParser::testSpec() { addFilter(); return m_testSpec; } void TestSpecParser::visitChar( char c ) { if( m_mode == None ) { switch( c ) { case ' ': return; case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); case '\\': return escape(); default: startNewMode( Name, m_pos ); break; } } if( m_mode == Name ) { if( c == ',' ) { addPattern(); addFilter(); } else if( c == '[' ) { if( subString() == "exclude:" ) m_exclusion = true; else addPattern(); startNewMode( Tag, ++m_pos ); } else if( c == '\\' ) escape(); } else if( m_mode == EscapedName ) m_mode = Name; else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) addPattern(); } void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { m_mode = mode; m_start = start; } void TestSpecParser::escape() { if( m_mode == None ) m_start = m_pos; m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } void TestSpecParser::addFilter() { if( !m_currentFilter.m_patterns.empty() ) { m_testSpec.m_filters.push_back( m_currentFilter ); m_currentFilter = TestSpec::Filter(); } } TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } } // namespace Catch // end catch_test_spec_parser.cpp // start catch_timer.cpp #include namespace Catch { auto getCurrentNanosecondsSinceEpoch() -> uint64_t { return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); } auto estimateClockResolution() -> uint64_t { uint64_t sum = 0; static const uint64_t iterations = 1000000; for( std::size_t i = 0; i < iterations; ++i ) { uint64_t ticks; uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); do { ticks = getCurrentNanosecondsSinceEpoch(); } while( ticks == baseTicks ); auto delta = ticks - baseTicks; sum += delta; } // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers // - and potentially do more iterations if there's a high variance. return sum/iterations; } auto getEstimatedClockResolution() -> uint64_t { static auto s_resolution = estimateClockResolution(); return s_resolution; } void Timer::start() { m_nanoseconds = getCurrentNanosecondsSinceEpoch(); } auto Timer::getElapsedNanoseconds() const -> unsigned int { return static_cast(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); } auto Timer::getElapsedMicroseconds() const -> unsigned int { return static_cast(getElapsedNanoseconds()/1000); } auto Timer::getElapsedMilliseconds() const -> unsigned int { return static_cast(getElapsedMicroseconds()/1000); } auto Timer::getElapsedSeconds() const -> double { return getElapsedMicroseconds()/1000000.0; } } // namespace Catch // end catch_timer.cpp // start catch_tostring.cpp #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" # pragma clang diagnostic ignored "-Wglobal-constructors" #endif #include namespace Catch { namespace Detail { const std::string unprintableString = "{?}"; namespace { const int hexThreshold = 255; struct Endianness { enum Arch { Big, Little }; static Arch which() { union _{ int asInt; char asChar[sizeof (int)]; } u; u.asInt = 1; return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; } }; } std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } unsigned char const *bytes = static_cast(object); std::ostringstream os; os << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) os << std::setw(2) << static_cast(bytes[i]); return os.str(); } } template std::string fpToString( T value, int precision ) { std::ostringstream oss; oss << std::setprecision( precision ) << std::fixed << value; std::string d = oss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) i++; d = d.substr( 0, i+1 ); } return d; } //// ======================================================= //// // // Out-of-line defs for full specialization of StringMaker // //// ======================================================= //// std::string StringMaker::convert(const std::string& str) { if (!getCurrentContext().getConfig()->showInvisibles()) { return '"' + str + '"'; } std::string s("\""); for (char c : str) { switch (c) { case '\n': s.append("\\n"); break; case '\t': s.append("\\t"); break; default: s.push_back(c); break; } } s.append("\""); return s; } std::string StringMaker::convert(const std::wstring& wstr) { std::string s; s.reserve(wstr.size()); for (auto c : wstr) { s += (c <= 0xff) ? static_cast(c) : '?'; } return ::Catch::Detail::stringify(s); } std::string StringMaker::convert(char const* str) { if (str) { return ::Catch::Detail::stringify(std::string{ str }); } else { return{ "{null string}" }; } } std::string StringMaker::convert(char* str) { if (str) { return ::Catch::Detail::stringify(std::string{ str }); } else { return{ "{null string}" }; } } std::string StringMaker::convert(wchar_t const * str) { if (str) { return ::Catch::Detail::stringify(std::wstring{ str }); } else { return{ "{null string}" }; } } std::string StringMaker::convert(wchar_t * str) { if (str) { return ::Catch::Detail::stringify(std::wstring{ str }); } else { return{ "{null string}" }; } } std::string StringMaker::convert(int value) { return ::Catch::Detail::stringify(static_cast(value)); } std::string StringMaker::convert(long value) { return ::Catch::Detail::stringify(static_cast(value)); } std::string StringMaker::convert(long long value) { std::ostringstream oss; oss << value; if (value > Detail::hexThreshold) { oss << " (0x" << std::hex << value << ')'; } return oss.str(); } std::string StringMaker::convert(unsigned int value) { return ::Catch::Detail::stringify(static_cast(value)); } std::string StringMaker::convert(unsigned long value) { return ::Catch::Detail::stringify(static_cast(value)); } std::string StringMaker::convert(unsigned long long value) { std::ostringstream oss; oss << value; if (value > Detail::hexThreshold) { oss << " (0x" << std::hex << value << ')'; } return oss.str(); } std::string StringMaker::convert(bool b) { return b ? "true" : "false"; } std::string StringMaker::convert(char value) { if (value == '\r') { return "'\\r'"; } else if (value == '\f') { return "'\\f'"; } else if (value == '\n') { return "'\\n'"; } else if (value == '\t') { return "'\\t'"; } else if ('\0' <= value && value < ' ') { return ::Catch::Detail::stringify(static_cast(value)); } else { char chstr[] = "' '"; chstr[1] = value; return chstr; } } std::string StringMaker::convert(signed char c) { return ::Catch::Detail::stringify(static_cast(c)); } std::string StringMaker::convert(unsigned char c) { return ::Catch::Detail::stringify(static_cast(c)); } std::string StringMaker::convert(std::nullptr_t) { return "nullptr"; } std::string StringMaker::convert(float value) { return fpToString(value, 5) + 'f'; } std::string StringMaker::convert(double value) { return fpToString(value, 10); } } // end namespace Catch #if defined(__clang__) # pragma clang diagnostic pop #endif // end catch_tostring.cpp // start catch_totals.cpp namespace Catch { Counts Counts::operator - ( Counts const& other ) const { Counts diff; diff.passed = passed - other.passed; diff.failed = failed - other.failed; diff.failedButOk = failedButOk - other.failedButOk; return diff; } Counts& Counts::operator += ( Counts const& other ) { passed += other.passed; failed += other.failed; failedButOk += other.failedButOk; return *this; } std::size_t Counts::total() const { return passed + failed + failedButOk; } bool Counts::allPassed() const { return failed == 0 && failedButOk == 0; } bool Counts::allOk() const { return failed == 0; } Totals Totals::operator - ( Totals const& other ) const { Totals diff; diff.assertions = assertions - other.assertions; diff.testCases = testCases - other.testCases; return diff; } Totals& Totals::operator += ( Totals const& other ) { assertions += other.assertions; testCases += other.testCases; return *this; } Totals Totals::delta( Totals const& prevTotals ) const { Totals diff = *this - prevTotals; if( diff.assertions.failed > 0 ) ++diff.testCases.failed; else if( diff.assertions.failedButOk > 0 ) ++diff.testCases.failedButOk; else ++diff.testCases.passed; return diff; } } // end catch_totals.cpp // start catch_version.cpp #include namespace Catch { Version::Version ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), patchNumber( _patchNumber ), branchName( _branchName ), buildNumber( _buildNumber ) {} std::ostream& operator << ( std::ostream& os, Version const& version ) { os << version.majorVersion << '.' << version.minorVersion << '.' << version.patchNumber; // branchName is never null -> 0th char is \0 if it is empty if (version.branchName[0]) { os << '-' << version.branchName << '.' << version.buildNumber; } return os; } Version const& libraryVersion() { static Version version( 2, 0, 1, "", 0 ); return version; } } // end catch_version.cpp // start catch_wildcard_pattern.cpp namespace Catch { WildcardPattern::WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_pattern( adjustCase( pattern ) ) { if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } } bool WildcardPattern::matches( std::string const& str ) const { switch( m_wildcard ) { case NoWildcard: return m_pattern == adjustCase( str ); case WildcardAtStart: return endsWith( adjustCase( str ), m_pattern ); case WildcardAtEnd: return startsWith( adjustCase( str ), m_pattern ); case WildcardAtBothEnds: return contains( adjustCase( str ), m_pattern ); default: CATCH_INTERNAL_ERROR( "Unknown enum" ); } } std::string WildcardPattern::adjustCase( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } } // end catch_wildcard_pattern.cpp // start catch_xmlwriter.cpp // start catch_xmlwriter.h #include #include namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); void encodeTo( std::ostream& os ) const; friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); private: std::string m_str; ForWhat m_forWhat; }; class XmlWriter { public: class ScopedElement { public: ScopedElement( XmlWriter* writer ); ScopedElement( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept; ~ScopedElement(); ScopedElement& writeText( std::string const& text, bool indent = true ); template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { m_writer->writeAttribute( name, attribute ); return *this; } private: mutable XmlWriter* m_writer = nullptr; }; XmlWriter( std::ostream& os = Catch::cout() ); ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; XmlWriter& operator=( XmlWriter const& ) = delete; XmlWriter& startElement( std::string const& name ); ScopedElement scopedElement( std::string const& name ); XmlWriter& endElement(); XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); XmlWriter& writeAttribute( std::string const& name, bool attribute ); template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { m_oss.clear(); m_oss.str(std::string()); m_oss << attribute; return writeAttribute( name, m_oss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ); XmlWriter& writeComment( std::string const& text ); void writeStylesheetRef( std::string const& url ); XmlWriter& writeBlankLine(); void ensureTagClosed(); private: void writeDeclaration(); void newlineIfNecessary(); bool m_tagIsOpen = false; bool m_needsNewline = false; std::vector m_tags; std::string m_indent; std::ostream& m_os; std::ostringstream m_oss; }; } // end catch_xmlwriter.h #include namespace Catch { XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) : m_str( str ), m_forWhat( forWhat ) {} void XmlEncode::encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) for( std::size_t i = 0; i < m_str.size(); ++ i ) { char c = m_str[i]; switch( c ) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: http://www.w3.org/TR/xml/#syntax if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) os << ">"; else os << c; break; case '\"': if( m_forWhat == ForAttributes ) os << """; else os << c; break; default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ); } else os << c; } } } std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { xmlEncode.encodeTo( os ); return os; } XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) : m_writer( writer ) {} XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept : m_writer( other.m_writer ){ other.m_writer = nullptr; } XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { if ( m_writer ) { m_writer->endElement(); } m_writer = other.m_writer; other.m_writer = nullptr; return *this; } XmlWriter::ScopedElement::~ScopedElement() { if( m_writer ) m_writer->endElement(); } XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { m_writer->writeText( text, indent ); return *this; } XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) { writeDeclaration(); } XmlWriter::~XmlWriter() { while( !m_tags.empty() ) endElement(); } XmlWriter& XmlWriter::startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; return *this; } XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { ScopedElement scoped( this ); startElement( name ); return scoped; } XmlWriter& XmlWriter::endElement() { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { m_os << "/>"; m_tagIsOpen = false; } else { m_os << m_indent << ""; } m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) m_os << m_indent; m_os << XmlEncode( text ); m_needsNewline = true; } return *this; } XmlWriter& XmlWriter::writeComment( std::string const& text ) { ensureTagClosed(); m_os << m_indent << ""; m_needsNewline = true; return *this; } void XmlWriter::writeStylesheetRef( std::string const& url ) { m_os << "\n"; } XmlWriter& XmlWriter::writeBlankLine() { ensureTagClosed(); m_os << '\n'; return *this; } void XmlWriter::ensureTagClosed() { if( m_tagIsOpen ) { m_os << ">" << std::endl; m_tagIsOpen = false; } } void XmlWriter::writeDeclaration() { m_os << "\n"; } void XmlWriter::newlineIfNecessary() { if( m_needsNewline ) { m_os << std::endl; m_needsNewline = false; } } } // end catch_xmlwriter.cpp // start catch_reporter_bases.cpp #include #include #include #include #include namespace Catch { void prepareExpandedExpression(AssertionResult& result) { result.getExpandedExpression(); } // Because formatting using c++ streams is stateful, drop down to C is required // Alternatively we could use stringstream, but its performance is... not good. std::string getFormattedDuration( double duration ) { // Max exponent + 1 is required to represent the whole part // + 1 for decimal point // + 3 for the 3 decimal places // + 1 for null terminator const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; char buffer[maxDoubleSize]; // Save previous errno, to prevent sprintf from overwriting it ErrnoGuard guard; #ifdef _MSC_VER sprintf_s(buffer, "%.3f", duration); #else sprintf(buffer, "%.3f", duration); #endif return std::string(buffer); } TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) :StreamingReporterBase(_config) {} void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} bool TestEventListenerBase::assertionEnded(AssertionStats const &) { return false; } } // end namespace Catch // end catch_reporter_bases.cpp // start catch_reporter_compact.cpp namespace { #ifdef CATCH_PLATFORM_MAC const char* failedString() { return "FAILED"; } const char* passedString() { return "PASSED"; } #else const char* failedString() { return "failed"; } const char* passedString() { return "passed"; } #endif // Colour::LightGrey Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } std::string bothOrAll( std::size_t count ) { return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } } namespace Catch { struct CompactReporter : StreamingReporterBase { using StreamingReporterBase::StreamingReporterBase; ~CompactReporter() override; static std::string getDescription() { return "Reports test results on a single line, suitable for IDEs"; } ReporterPreferences getPreferences() const override { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } void noMatchingTestCases( std::string const& spec ) override { stream << "No test cases matched '" << spec << '\'' << std::endl; } void assertionStarting( AssertionInfo const& ) override {} bool assertionEnded( AssertionStats const& _assertionStats ) override { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } void sectionEnded(SectionStats const& _sectionStats) override { if (m_config->showDurations() == ShowDurations::Always) { stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } void testRunEnded( TestRunStats const& _testRunStats ) override { printTotals( _testRunStats.totals ); stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { public: AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( _printInfoMessages ) {} void print() { printSourceInfo(); itMessage = messages.begin(); switch( result.getResultType() ) { case ResultWas::Ok: printResultType( Colour::ResultSuccess, passedString() ); printOriginalExpression(); printReconstructedExpression(); if ( ! result.hasExpression() ) printRemainingMessages( Colour::None ); else printRemainingMessages(); break; case ResultWas::ExpressionFailed: if( result.isOk() ) printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); else printResultType( Colour::Error, failedString() ); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; case ResultWas::ThrewException: printResultType( Colour::Error, failedString() ); printIssue( "unexpected exception with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::FatalErrorCondition: printResultType( Colour::Error, failedString() ); printIssue( "fatal error condition with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::DidntThrowException: printResultType( Colour::Error, failedString() ); printIssue( "expected exception, got none" ); printExpressionWas(); printRemainingMessages(); break; case ResultWas::Info: printResultType( Colour::None, "info" ); printMessage(); printRemainingMessages(); break; case ResultWas::Warning: printResultType( Colour::None, "warning" ); printMessage(); printRemainingMessages(); break; case ResultWas::ExplicitFailure: printResultType( Colour::Error, failedString() ); printIssue( "explicitly" ); printRemainingMessages( Colour::None ); break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: printResultType( Colour::Error, "** internal error **" ); break; } } private: void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ':'; } void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); stream << ' ' << passOrFail; } stream << ':'; } } void printIssue( std::string const& issue ) const { stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; } printOriginalExpression(); } } void printOriginalExpression() const { if( result.hasExpression() ) { stream << ' ' << result.getExpression(); } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { { Colour colour( dimColour() ); stream << " for: "; } stream << result.getExpandedExpression(); } } void printMessage() { if ( itMessage != messages.end() ) { stream << " '" << itMessage->message << '\''; ++itMessage; } } void printRemainingMessages( Colour::Code colour = dimColour() ) { if ( itMessage == messages.end() ) return; // using messages.end() directly yields (or auto) compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); { Colour colourGuard( colour ); stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; } } } } private: std::ostream& stream; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; }; // Colour, message variants: // - white: No tests ran. // - red: Failed [both/all] N test cases, failed [both/all] M assertions. // - white: Passed [both/all] N test cases (no assertions). // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "No tests ran."; } else if( totals.testCases.failed == totals.testCases.total() ) { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << "Passed " << bothOrAll( totals.testCases.total() ) << pluralise( totals.testCases.total(), "test case" ) << " (no assertions)."; } else if( totals.assertions.failed ) { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; CompactReporter::~CompactReporter() {} CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch // end catch_reporter_compact.cpp // start catch_reporter_console.cpp #include #include #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch // Note that 4062 (not all labels are handled // and default is missing) is enabled #endif namespace Catch { namespace { std::size_t makeRatio( std::size_t number, std::size_t total ) { std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; return ( ratio == 0 && number > 0 ) ? 1 : ratio; } std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { if( i > j && i > k ) return i; else if( j > k ) return j; else return k; } struct ColumnInfo { enum Justification { Left, Right }; std::string name; int width; Justification justification; }; struct ColumnBreak {}; struct RowBreak {}; class TablePrinter { std::ostream& m_os; std::vector m_columnInfos; std::ostringstream m_oss; int m_currentColumn = -1; bool m_isOpen = false; public: TablePrinter( std::ostream& os, std::vector const& columnInfos ) : m_os( os ), m_columnInfos( columnInfos ) {} auto columnInfos() const -> std::vector const& { return m_columnInfos; } void open() { if( !m_isOpen ) { m_isOpen = true; *this << RowBreak(); for( auto const& info : m_columnInfos ) *this << info.name << ColumnBreak(); *this << RowBreak(); m_os << Catch::getLineOfChars<'-'>() << "\n"; } } void close() { if( m_isOpen ) { *this << RowBreak(); m_os << std::endl; m_isOpen = false; } } template friend TablePrinter& operator << ( TablePrinter& tp, T const& value ) { tp.m_oss << value; return tp; } friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { auto colStr = tp.m_oss.str(); // This takes account of utf8 encodings auto strSize = Catch::StringRef( colStr ).numberOfCharacters(); tp.m_oss.str(""); tp.open(); if( tp.m_currentColumn == static_cast(tp.m_columnInfos.size()-1) ) { tp.m_currentColumn = -1; tp.m_os << "\n"; } tp.m_currentColumn++; auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; auto padding = ( strSize+2 < static_cast( colInfo.width ) ) ? std::string( colInfo.width-(strSize+2), ' ' ) : std::string(); if( colInfo.justification == ColumnInfo::Left ) tp.m_os << colStr << padding << " "; else tp.m_os << padding << colStr << " "; return tp; } friend TablePrinter& operator << ( TablePrinter& tp, RowBreak ) { if( tp.m_currentColumn > 0 ) { tp.m_os << "\n"; tp.m_currentColumn = -1; } return tp; } }; class Duration { enum class Unit { Auto, Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes }; static const uint64_t s_nanosecondsInAMicrosecond = 1000; static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; uint64_t m_inNanoseconds; Unit m_units; public: Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) : m_inNanoseconds( inNanoseconds ), m_units( units ) { if( m_units == Unit::Auto ) { if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) m_units = Unit::Nanoseconds; else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) m_units = Unit::Microseconds; else if( m_inNanoseconds < s_nanosecondsInASecond ) m_units = Unit::Milliseconds; else if( m_inNanoseconds < s_nanosecondsInAMinute ) m_units = Unit::Seconds; else m_units = Unit::Minutes; } } auto value() const -> double { switch( m_units ) { case Unit::Microseconds: return m_inNanoseconds / static_cast( s_nanosecondsInAMicrosecond ); case Unit::Milliseconds: return m_inNanoseconds / static_cast( s_nanosecondsInAMillisecond ); case Unit::Seconds: return m_inNanoseconds / static_cast( s_nanosecondsInASecond ); case Unit::Minutes: return m_inNanoseconds / static_cast( s_nanosecondsInAMinute ); default: return static_cast( m_inNanoseconds ); } } auto unitsAsString() const -> std::string { switch( m_units ) { case Unit::Nanoseconds: return "ns"; case Unit::Microseconds: return "µs"; case Unit::Milliseconds: return "ms"; case Unit::Seconds: return "s"; case Unit::Minutes: return "m"; default: return "** internal error **"; } } friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { return os << duration.value() << " " << duration.unitsAsString(); } }; } // end anon namespace struct ConsoleReporter : StreamingReporterBase { TablePrinter m_tablePrinter; ConsoleReporter( ReporterConfig const& config ) : StreamingReporterBase( config ), m_tablePrinter( config.stream(), { { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-32, ColumnInfo::Left }, { "iters", 8, ColumnInfo::Right }, { "elapsed ns", 14, ColumnInfo::Right }, { "average", 14, ColumnInfo::Right } } ) {} ~ConsoleReporter() override; static std::string getDescription() { return "Reports test results as plain lines of text"; } void noMatchingTestCases( std::string const& spec ) override { stream << "No test cases matched '" << spec << '\'' << std::endl; } void assertionStarting( AssertionInfo const& ) override { } bool assertionEnded( AssertionStats const& _assertionStats ) override { AssertionResult const& result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); // Drop out if result was successful but we're not printing them. if( !includeResults && result.getResultType() != ResultWas::Warning ) return false; lazyPrint(); AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; } void sectionStarting( SectionInfo const& _sectionInfo ) override { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } void sectionEnded( SectionStats const& _sectionStats ) override { m_tablePrinter.close(); if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); if( m_sectionStack.size() > 1 ) stream << "\nNo assertions in section"; else stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } if( m_config->showDurations() == ShowDurations::Always ) { stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if( m_headerPrinted ) { m_headerPrinted = false; } StreamingReporterBase::sectionEnded( _sectionStats ); } void benchmarkStarting( BenchmarkInfo const& info ) override { lazyPrintWithoutClosingBenchmarkTable(); auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); bool firstLine = true; for( auto line : nameCol ) { if( !firstLine ) m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); else firstLine = false; m_tablePrinter << line << ColumnBreak(); } } void benchmarkEnded( BenchmarkStats const& stats ) override { Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); m_tablePrinter << stats.iterations << ColumnBreak() << stats.elapsedTimeInNanoseconds << ColumnBreak() << average << ColumnBreak(); } void testCaseEnded( TestCaseStats const& _testCaseStats ) override { m_tablePrinter.close(); StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } void testGroupEnded( TestGroupStats const& _testGroupStats ) override { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } void testRunEnded( TestRunStats const& _testRunStats ) override { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { public: AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), result( _stats.assertionResult ), colour( Colour::None ), message( result.getMessage() ), messages( _stats.infoMessages ), printInfoMessages( _printInfoMessages ) { switch( result.getResultType() ) { case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"; //if( result.hasMessage() ) if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ExpressionFailed: if( result.isOk() ) { colour = Colour::Success; passOrFail = "FAILED - but was ok"; } else { colour = Colour::Error; passOrFail = "FAILED"; } if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to unexpected exception with "; if (_stats.infoMessages.size() == 1) messageLabel += "message"; if (_stats.infoMessages.size() > 1) messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to a fatal error condition"; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "because no exception was thrown where one was expected"; break; case ResultWas::Info: messageLabel = "info"; break; case ResultWas::Warning: messageLabel = "warning"; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"; colour = Colour::Error; if( _stats.infoMessages.size() == 1 ) messageLabel = "explicitly with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "explicitly with messages"; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: passOrFail = "** internal error **"; colour = Colour::Error; break; } } void print() const { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { stream << '\n'; } printMessage(); } private: void printResultType() const { if( !passOrFail.empty() ) { Colour colourGuard( colour ); stream << passOrFail << ":\n"; } } void printOriginalExpression() const { if( result.hasExpression() ) { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); stream << Column( result.getExpandedExpression() ).indent(2) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ':' << '\n'; for( auto const& msg : messages ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || msg.type != ResultWas::Info ) stream << Column( msg.message ).indent(2) << '\n'; } } void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ": "; } std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; Colour::Code colour; std::string passOrFail; std::string messageLabel; std::string message; std::vector messages; bool printInfoMessages; }; void lazyPrint() { m_tablePrinter.close(); lazyPrintWithoutClosingBenchmarkTable(); } void lazyPrintWithoutClosingBenchmarkTable() { if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) lazyPrintGroupInfo(); if( !m_headerPrinted ) { printTestCaseAndSectionHeader(); m_headerPrinted = true; } } void lazyPrintRunInfo() { stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; currentTestRunInfo.used = true; } void lazyPrintGroupInfo() { if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { printClosedHeader( "Group: " + currentGroupInfo->name ); currentGroupInfo.used = true; } } void printTestCaseAndSectionHeader() { assert( !m_sectionStack.empty() ); printOpenHeader( currentTestCaseInfo->name ); if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); auto it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) printHeaderString( it->name, 2 ); } SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); stream << lineInfo << '\n'; } stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); } } // if string has a : in first line will set indent to follow it on // subsequent lines void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { std::size_t i = _string.find( ": " ); if( i != std::string::npos ) i+=2; else i = 0; stream << Column( _string ).indent( indent+i ).initialIndent( indent ) << '\n'; } struct SummaryColumn { SummaryColumn( std::string const& _label, Colour::Code _colour ) : label( _label ), colour( _colour ) {} SummaryColumn addRow( std::size_t count ) { std::ostringstream oss; oss << count; std::string row = oss.str(); for( auto& oldRow : rows ) { while( oldRow.size() < row.size() ) oldRow = ' ' + oldRow; while( oldRow.size() > row.size() ) row = ' ' + row; } rows.push_back( row ); return *this; } std::string label; Colour::Code colour; std::vector rows; }; void printTotals( Totals const& totals ) { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.testCases.passed, "test case" ) << ')' << '\n'; } else { std::vector columns; columns.push_back( SummaryColumn( "", Colour::None ) .addRow( totals.testCases.total() ) .addRow( totals.assertions.total() ) ); columns.push_back( SummaryColumn( "passed", Colour::Success ) .addRow( totals.testCases.passed ) .addRow( totals.assertions.passed ) ); columns.push_back( SummaryColumn( "failed", Colour::ResultError ) .addRow( totals.testCases.failed ) .addRow( totals.assertions.failed ) ); columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) .addRow( totals.testCases.failedButOk ) .addRow( totals.assertions.failedButOk ) ); printSummaryRow( "test cases", columns, 0 ); printSummaryRow( "assertions", columns, 1 ); } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { for( auto col : cols ) { std::string value = col.rows[row]; if( col.label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; else stream << Colour( Colour::Warning ) << "- none -"; } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( col.colour ) << value << ' ' << col.label; } } stream << '\n'; } void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )++; while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )--; stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); if( totals.testCases.allPassed() ) stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); else stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); } else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } stream << '\n'; } void printSummaryDivider() { stream << getLineOfChars<'-'>() << '\n'; } private: bool m_headerPrinted = false; }; CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) ConsoleReporter::~ConsoleReporter() {} } // end namespace Catch #if defined(_MSC_VER) #pragma warning(pop) #endif // end catch_reporter_console.cpp // start catch_reporter_junit.cpp #include #include #include namespace Catch { namespace { std::string getCurrentTimestamp() { // Beware, this is not reentrant because of backward compatibility issues // Also, UTC only, again because of backward compatibility (%z is C++11) time_t rawtime; std::time(&rawtime); auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER std::tm timeInfo = {}; gmtime_s(&timeInfo, &rawtime); #else std::tm* timeInfo; timeInfo = std::gmtime(&rawtime); #endif char timeStamp[timeStampSize]; const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif return std::string(timeStamp); } std::string fileNameTag(const std::vector &tags) { auto it = std::find_if(begin(tags), end(tags), [] (std::string const& tag) {return tag.front() == '#'; }); if (it != tags.end()) return it->substr(1); return std::string(); } } class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; } ~JunitReporter() override; static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } void noMatchingTestCases( std::string const& /*spec*/ ) override {} void testRunStarting( TestRunInfo const& runInfo ) override { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } void testGroupStarting( GroupInfo const& groupInfo ) override { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); unexpectedExceptions = 0; CumulativeReporterBase::testGroupStarting( groupInfo ); } void testCaseStarting( TestCaseInfo const& testCaseInfo ) override { m_okToFail = testCaseInfo.okToFail(); } bool assertionEnded( AssertionStats const& assertionStats ) override { if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } void testCaseEnded( TestCaseStats const& testCaseStats ) override { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } void testGroupEnded( TestGroupStats const& testGroupStats ) override { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } void testRunEndedCumulative() override { xml.endElement(); } void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); TestGroupStats const& stats = groupNode.value; xml.writeAttribute( "name", stats.groupInfo.name ); xml.writeAttribute( "errors", unexpectedExceptions ); xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); xml.writeAttribute( "tests", stats.totals.assertions.total() ); xml.writeAttribute( "hostname", "tbd" ); // !TBD if( m_config->showDurations() == ShowDurations::Never ) xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( auto const& child : groupNode.children ) writeTestCase( *child ); xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); } void writeTestCase( TestCaseNode const& testCaseNode ) { TestCaseStats const& stats = testCaseNode.value; // All test cases have exactly one section - which represents the // test case itself. That section may have 0-n nested sections assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); std::string className = stats.testInfo.className; if( className.empty() ) { className = fileNameTag(stats.testInfo.tags); if ( className.empty() ) className = "global"; } if ( !m_config->name().empty() ) className = m_config->name() + "." + className; writeSection( className, "", rootSection ); } void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); if( !sectionNode.stdErr.empty() ) xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } for( auto const& childNode : sectionNode.childSections ) if( className.empty() ) writeSection( name, "", *childNode ); else writeSection( className, name, *childNode ); } void writeAssertions( SectionNode const& sectionNode ) { for( auto const& assertion : sectionNode.assertions ) writeAssertion( assertion ); } void writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; if( !result.isOk() ) { std::string elementName; switch( result.getResultType() ) { case ResultWas::ThrewException: case ResultWas::FatalErrorCondition: elementName = "error"; break; case ResultWas::ExplicitFailure: elementName = "failure"; break; case ResultWas::ExpressionFailed: elementName = "failure"; break; case ResultWas::DidntThrowException: elementName = "failure"; break; // We should never see these here: case ResultWas::Info: case ResultWas::Warning: case ResultWas::Ok: case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: elementName = "internalError"; break; } XmlWriter::ScopedElement e = xml.scopedElement( elementName ); xml.writeAttribute( "message", result.getExpandedExpression() ); xml.writeAttribute( "type", result.getTestMacroName() ); std::ostringstream oss; if( !result.getMessage().empty() ) oss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) oss << msg.message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); } } XmlWriter xml; Timer suiteTimer; std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions = 0; bool m_okToFail = false; }; JunitReporter::~JunitReporter() {} CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch // end catch_reporter_junit.cpp // start catch_reporter_multi.cpp namespace Catch { void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { m_reporters.push_back( std::move( reporter ) ); } ReporterPreferences MultipleReporters::getPreferences() const { return m_reporters[0]->getPreferences(); } std::set MultipleReporters::getSupportedVerbosities() { return std::set{ }; } void MultipleReporters::noMatchingTestCases( std::string const& spec ) { for( auto const& reporter : m_reporters ) reporter->noMatchingTestCases( spec ); } void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { for( auto const& reporter : m_reporters ) reporter->benchmarkStarting( benchmarkInfo ); } void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { for( auto const& reporter : m_reporters ) reporter->benchmarkEnded( benchmarkStats ); } void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { for( auto const& reporter : m_reporters ) reporter->testRunStarting( testRunInfo ); } void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { for( auto const& reporter : m_reporters ) reporter->testGroupStarting( groupInfo ); } void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { for( auto const& reporter : m_reporters ) reporter->testCaseStarting( testInfo ); } void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { for( auto const& reporter : m_reporters ) reporter->sectionStarting( sectionInfo ); } void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { for( auto const& reporter : m_reporters ) reporter->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { bool clearBuffer = false; for( auto const& reporter : m_reporters ) clearBuffer |= reporter->assertionEnded( assertionStats ); return clearBuffer; } void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { for( auto const& reporter : m_reporters ) reporter->sectionEnded( sectionStats ); } void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { for( auto const& reporter : m_reporters ) reporter->testCaseEnded( testCaseStats ); } void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { for( auto const& reporter : m_reporters ) reporter->testGroupEnded( testGroupStats ); } void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { for( auto const& reporter : m_reporters ) reporter->testRunEnded( testRunStats ); } void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { for( auto const& reporter : m_reporters ) reporter->skipTest( testInfo ); } bool MultipleReporters::isMulti() const { return true; } } // end namespace Catch // end catch_reporter_multi.cpp // start catch_reporter_xml.cpp #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch // Note that 4062 (not all labels are handled // and default is missing) is enabled #endif namespace Catch { class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_xml(_config.stream()) { m_reporterPrefs.shouldRedirectStdOut = true; } ~XmlReporter() override; static std::string getDescription() { return "Reports test results as an XML document"; } virtual std::string getStylesheetRef() const { return std::string(); } void writeSourceInfo( SourceLineInfo const& sourceInfo ) { m_xml .writeAttribute( "filename", sourceInfo.file ) .writeAttribute( "line", sourceInfo.line ); } public: // StreamingReporterBase void noMatchingTestCases( std::string const& s ) override { StreamingReporterBase::noMatchingTestCases( s ); } void testRunStarting( TestRunInfo const& testInfo ) override { StreamingReporterBase::testRunStarting( testInfo ); std::string stylesheetRef = getStylesheetRef(); if( !stylesheetRef.empty() ) m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); } void testGroupStarting( GroupInfo const& groupInfo ) override { StreamingReporterBase::testGroupStarting( groupInfo ); m_xml.startElement( "Group" ) .writeAttribute( "name", groupInfo.name ); } void testCaseStarting( TestCaseInfo const& testInfo ) override { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ) .writeAttribute( "name", trim( testInfo.name ) ) .writeAttribute( "description", testInfo.description ) .writeAttribute( "tags", testInfo.tagsAsString() ); writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); m_xml.ensureTagClosed(); } void sectionStarting( SectionInfo const& sectionInfo ) override { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); writeSourceInfo( sectionInfo.lineInfo ); m_xml.ensureTagClosed(); } } void assertionStarting( AssertionInfo const& ) override { } bool assertionEnded( AssertionStats const& assertionStats ) override { AssertionResult const& result = assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); if( includeResults ) { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { if( msg.type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) .writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) .writeText( msg.message ); } } } // Drop out if result was successful but we're not printing them. if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. if( result.hasExpression() ) { m_xml.startElement( "Expression" ) .writeAttribute( "success", result.succeeded() ) .writeAttribute( "type", result.getTestMacroName() ); writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. switch( result.getResultType() ) { case ResultWas::ThrewException: m_xml.startElement( "Exception" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; case ResultWas::FatalErrorCondition: m_xml.startElement( "FatalErrorCondition" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: m_xml.startElement( "Failure" ); writeSourceInfo( result.getSourceInfo() ); m_xml.writeText( result.getMessage() ); m_xml.endElement(); break; default: break; } if( result.hasExpression() ) m_xml.endElement(); return true; } void sectionEnded( SectionStats const& sectionStats ) override { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); e.writeAttribute( "successes", sectionStats.assertions.passed ); e.writeAttribute( "failures", sectionStats.assertions.failed ); e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); m_xml.endElement(); } } void testCaseEnded( TestCaseStats const& testCaseStats ) override { StreamingReporterBase::testCaseEnded( testCaseStats ); XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); if( !testCaseStats.stdOut.empty() ) m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); if( !testCaseStats.stdErr.empty() ) m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); m_xml.endElement(); } void testGroupEnded( TestGroupStats const& testGroupStats ) override { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); m_xml.endElement(); } void testRunEnded( TestRunStats const& testRunStats ) override { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); m_xml.endElement(); } private: Timer m_testCaseTimer; XmlWriter m_xml; int m_sectionDepth = 0; }; XmlReporter::~XmlReporter() {} CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch #if defined(_MSC_VER) #pragma warning(pop) #endif // end catch_reporter_xml.cpp namespace Catch { LeakDetector leakDetector; } #ifdef __clang__ #pragma clang diagnostic pop #endif // end catch_impl.hpp #endif #ifdef CATCH_CONFIG_MAIN // start catch_default_main.hpp #ifndef __OBJC__ #if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { #else // Standard C/C++ main entry point int main (int argc, char * argv[]) { #endif return Catch::Session().run( argc, argv ); } #else // __OBJC__ // Objective-C entry point int main (int argc, char * const argv[]) { #if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #endif Catch::registerTestMethods(); int result = Catch::Session().run( argc, (char**)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif return result; } #endif // __OBJC__ // end catch_default_main.hpp #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif #if !defined(CATCH_CONFIG_DISABLE) ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) #endif// CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() // "BDD-style" convenience wrappers #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) #define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) #define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) #define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc ) #define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) #define THEN( desc ) SECTION( std::string(" Then: ") + desc ) #define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) using Catch::Detail::Approx; #else ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL #define CATCH_REQUIRE( ... ) (void)(0) #define CATCH_REQUIRE_FALSE( ... ) (void)(0) #define CATCH_REQUIRE_THROWS( ... ) (void)(0) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) #endif// CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) #define CATCH_CHECK( ... ) (void)(0) #define CATCH_CHECK_FALSE( ... ) (void)(0) #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) #define CATCH_CHECK_NOFAIL( ... ) (void)(0) #define CATCH_CHECK_THROWS( ... ) (void)(0) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_CHECK_NOTHROW( ... ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CATCH_CHECK_THAT( arg, matcher ) (void)(0) #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CATCH_INFO( msg ) (void)(0) #define CATCH_WARN( msg ) (void)(0) #define CATCH_CAPTURE( msg ) (void)(0) #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) #define CATCH_FAIL( ... ) (void)(0) #define CATCH_FAIL_CHECK( ... ) (void)(0) #define CATCH_SUCCEED( ... ) (void)(0) #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) // "BDD-style" convenience wrappers #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) #define CATCH_GIVEN( desc ) #define CATCH_WHEN( desc ) #define CATCH_AND_WHEN( desc ) #define CATCH_THEN( desc ) #define CATCH_AND_THEN( desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else #define REQUIRE( ... ) (void)(0) #define REQUIRE_FALSE( ... ) (void)(0) #define REQUIRE_THROWS( ... ) (void)(0) #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) #define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define REQUIRE_NOTHROW( ... ) (void)(0) #define CHECK( ... ) (void)(0) #define CHECK_FALSE( ... ) (void)(0) #define CHECKED_IF( ... ) if (__VA_ARGS__) #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) #define CHECK_NOFAIL( ... ) (void)(0) #define CHECK_THROWS( ... ) (void)(0) #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) #define CHECK_THROWS_WITH( expr, matcher ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define CHECK_NOTHROW( ... ) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) #define CHECK_THAT( arg, matcher ) (void)(0) #define REQUIRE_THAT( arg, matcher ) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS #define INFO( msg ) (void)(0) #define WARN( msg ) (void)(0) #define CAPTURE( msg ) (void)(0) #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #define METHOD_AS_TEST_CASE( method, ... ) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) #define FAIL( ... ) (void)(0) #define FAIL_CHECK( ... ) (void)(0) #define SUCCEED( ... ) (void)(0) #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // "BDD-style" convenience wrappers #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) #define GIVEN( desc ) #define WHEN( desc ) #define AND_WHEN( desc ) #define THEN( desc ) #define AND_THEN( desc ) using Catch::Detail::Approx; #endif // start catch_reenable_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(pop) # else # pragma clang diagnostic pop # endif #elif defined __GNUC__ # pragma GCC diagnostic pop #endif // end catch_reenable_warnings.h // end catch.hpp #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED segyio-1.5.2/external/catch2/dummy.cpp0000664000372000037200000000000013253720451017235 0ustar travistravissegyio-1.5.2/external/catch2/CMakeLists.txt0000664000372000037200000000040313253720451020145 0ustar travistraviscmake_minimum_required(VERSION 2.8.12) project(catch2 CXX) # Dummy source file added because INTERFACE type # library is not available in CMake 2.8.12 add_library(catch2 dummy.cpp) target_include_directories(catch2 SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) segyio-1.5.2/requirements.txt0000664000372000037200000000001613253720451015703 0ustar travistravisnumpy >= 1.10 segyio-1.5.2/MANIFEST.in0000664000372000037200000000007713253720451014164 0ustar travistravisinclude License.md include README.md recursive-include src *.h segyio-1.5.2/.pyup.yml0000664000372000037200000000017313253720451014221 0ustar travistravis# autogenerated pyup.io config file # see https://pyup.io/docs/configuration/ for all available options update: insecure segyio-1.5.2/CMakeLists.txt0000664000372000037200000001050713253720451015165 0ustar travistraviscmake_minimum_required(VERSION 2.8.12) project(segyio) include(CheckFunctionExists) include(CheckIncludeFile) include(CTest) include(GNUInstallDirs) if (DEFINED ENV{SEGYIO_NO_GIT_VER}) set(SEGYIO_NO_GIT_VER CACHE BOOL "Ignore version from git" ON) endif () if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git AND NOT SEGYIO_NO_GIT_VER) find_program(git-bin git) execute_process(COMMAND ${git-bin} describe --tags OUTPUT_VARIABLE git-describe OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE describe-failure WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) if (NOT describe-failure) message(STATUS "Found version ${git-describe} from git") else () message(STATUS "No version from git - falling back to 0.0.0") set(git-describe 0.0.0) endif () string(REGEX REPLACE "^v" "" ver-describe "${git-describe}") unset(git-bin) elseif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/python/segyio/version.py) # tarball checkout - the version file should've been written when the # tarball was built file(READ python/segyio/version.py ver-describe) string(REGEX REPLACE "^version =" "" ver-describe ${ver-describe}) string(REGEX REPLACE "'" "" ver-describe ${ver-describe}) string(STRIP ${ver-describe} ver-describe) message(STATUS "Found version ${ver-describe} from segyio/version.py") else () set(ver-describe 0.0.0) message(STATUS "Could not find version, guessing ${ver-describe}") endif () string(REPLACE . ";" version-list ${ver-describe}) list(GET version-list 0 segyio-major) list(GET version-list 1 segyio-minor) list(GET version-list 2 segyio-patch) unset(version-list) # versions can always be overriden with -Dsegyio_MAJOR=N set(segyio_MAJOR ${segyio-major} CACHE STRING "Major version") set(segyio_MINOR ${segyio-minor} CACHE STRING "Minor version") set(segyio_PATCH ${segyio-patch} CACHE STRING "Patch") set(segyio_VERSION ${segyio_MAJOR}.${segyio_MINOR}.${segyio_PATCH}) message(STATUS "segyio version ${segyio_VERSION}") if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif () option(BUILD_SHARED_LIBS "Build language bindings shared" OFF) option(BUILD_PYTHON "Build Python library" ON) option(REQUIRE_PYTHON "Fail cmake if python cannot be built" OFF) option(BUILD_MEX "Build Matlab mex files" OFF) check_include_file(netinet/in.h HAVE_NETINET_IN_H) check_include_file(arpa/inet.h HAVE_ARPA_INET_H) check_include_file(winsock2.h HAVE_WINSOCK2_H) check_include_file(getopt.h HAVE_GETOPT_H) check_include_file(sys/mman.h HAVE_SYS_MMAN_H) check_include_file(sys/stat.h HAVE_SYS_STAT_H) check_function_exists(getopt_long HAVE_GETOPT_LONG) if (HAVE_NETINET_IN_H) list(APPEND htons -DHAVE_NETINET_IN_H) elseif (HAVE_ARPA_INET_H) list(APPEND htons -DHAVE_ARPA_INET_H) elseif (HAVE_WINSOCK2_H) set(ws2 ws2_32) list(APPEND htons -DHAVE_WINSOCK2_H) else() message(FATAL_ERROR "Could not find htons") endif() if (HAVE_SYS_MMAN_H) list(APPEND mmap -DHAVE_MMAP) endif() if (HAVE_SYS_STAT_H) list(APPEND fstat -DHAVE_SYS_STAT_H) check_function_exists(_fstati64 HAVE_FSTATI64) if (HAVE_FSTATI64) list(APPEND fstat -DHAVE_FSTATI64) endif () check_function_exists(_ftelli64 HAVE_FTELLI64) if (HAVE_FTELLI64) list(APPEND fstat -DHAVE_FTELLI64) endif () else() message(FATAL_ERROR "Could not find sys/stat.h (fstat/ftelli)") endif() check_function_exists(ftello HAVE_FTELLO) if (HAVE_FTELLO) list(APPEND ftello -DHAVE_FTELLO) endif () if(NOT MSVC) set(m m) endif() if (NOT MSVC) # assuming gcc-style options set(c99 -std=c99) set(c++11 -std=c++11) # add warnings in debug mode list(APPEND warnings-c -Wall -Wextra -pedantic -Wformat-nonliteral -Wcast-align -Wpointer-arith -Wmissing-declarations -Wcast-qual -Wwrite-strings -Wchar-subscripts -Wredundant-decls ) else() list(APPEND warnings-c /W3 /wd4996) endif () set(testdata ${CMAKE_CURRENT_SOURCE_DIR}/test-data) add_subdirectory(external/catch2) add_subdirectory(lib) # language bindings add_subdirectory(mex) add_subdirectory(python) add_subdirectory(applications) add_subdirectory(man) segyio-1.5.2/License.md0000664000372000037200000001673113253720451014336 0ustar travistravis### GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. #### 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. #### 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. #### 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: - a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or - b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. #### 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: - a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. - b) Accompany the object code with a copy of the GNU GPL and this license document. #### 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: - a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. - b) Accompany the Combined Work with a copy of the GNU GPL and this license document. - c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. - d) Do one of the following: - 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. - 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. - e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) #### 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: - a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. - b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. #### 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. segyio-1.5.2/lib/0000775000372000037200000000000013253720451013170 5ustar travistravissegyio-1.5.2/lib/include/0000775000372000037200000000000013253720451014613 5ustar travistravissegyio-1.5.2/lib/include/segyio/0000775000372000037200000000000013253720451016112 5ustar travistravissegyio-1.5.2/lib/include/segyio/segy.h0000664000372000037200000004701513253720451017241 0ustar travistravis#ifndef SEGYIO_SEGY_H #define SEGYIO_SEGY_H #include #include #include #define SEGY_BINARY_HEADER_SIZE 400 #define SEGY_TEXT_HEADER_SIZE 3200 #define SEGY_TRACE_HEADER_SIZE 240 #ifdef __cplusplus extern "C" { #endif // __cplusplus /* * About signatures: * If a function returns `int` you can assume the return value is an error * code. 0 will always indicate success. If a function returns something else * than an int it's typically an operation that cannot fail assuming the passed * buffer is of the correct size. Any exceptions will be clearly stated. * * Function signatures are typically: * 1) input parameters * 2) output parameters * 3) low-level file structure information * * Output parameters are non-const pointers, input parameters are const * pointers or plain values. All functions are namespace-prefix'd with segy_. * Some functions return values, notably the family concerned with the binary * header such as segy_trace0, that should be used in consecutive segy function * calls that use the same name for one of its parameters. */ struct segy_file_handle; typedef struct segy_file_handle segy_file; segy_file* segy_open( const char* path, const char* mode ); int segy_mmap( segy_file* ); int segy_flush( segy_file*, bool async ); int segy_close( segy_file* ); /* binary header operations */ /* * The binheader buffer passed to these functions must be of *at least* * `segy_binheader_size`. Returns size, not an error code. */ int segy_binheader_size( void ); int segy_binheader( segy_file*, char* buf ); int segy_write_binheader( segy_file*, const char* buf ); /* * exception: the int returned is the number of samples (the segy standard only * allocates 2 octets for this, so it comfortably sits inside an int */ int segy_samples( const char* binheader ); int segy_sample_interval( segy_file*, float fallback , float* dt ); /* exception: the int returned is an enum, SEGY_SORTING, not an error code */ int segy_format( const char* binheader ); int segy_get_field( const char* traceheader, int field, int32_t* f ); int segy_get_bfield( const char* binheader, int field, int32_t* f ); int segy_set_field( char* traceheader, int field, int32_t val ); int segy_set_bfield( char* binheader, int field, int32_t val ); int segy_field_forall( segy_file*, int field, int start, int stop, int step, int* buf, long trace0, int trace_bsize ); /* * exception: segy_trace_bsize computes the size of the traces in bytes. Cannot * fail. */ int segy_trace_bsize( int samples ); /* byte-offset of the first trace header. */ long segy_trace0( const char* binheader ); /* * number of traces in this file. * if this function fails, the input argument is not modified. */ int segy_traces( segy_file*, int*, long trace0, int trace_bsize ); int segy_sample_indices( segy_file*, float t0, float dt, int count, float* buf ); /* text header operations */ /* buf in all read functions should be minimum segy_textheader_size() in size */ /* all read_textheader function outputs are zero-terminated C strings */ int segy_read_textheader( segy_file*, char *buf); int segy_textheader_size( void ); /* * read the extended textual headers. `pos = 0` gives the first *extended* * header, i.e. the first textual header following the binary header. * Behaviour is undefined if the file does not have extended headers */ int segy_read_ext_textheader( segy_file*, int pos, char* buf ); int segy_write_textheader( segy_file*, int pos, const char* buf ); /* Read the trace header at `traceno` into `buf`. */ int segy_traceheader( segy_file*, int traceno, char* buf, long trace0, int trace_bsize ); /* Read the trace header at `traceno` into `buf`. */ int segy_write_traceheader( segy_file*, int traceno, const char* buf, long trace0, int trace_bsize ); /* * The sorting type will be written to `sorting` if the function can figure out * how the file is sorted. */ int segy_sorting( segy_file*, int il, int xl, int tr_offset, int* sorting, long trace0, int trace_bsize ); /* * Number of offsets in this file, written to `offsets`. 1 if a 3D data set, >1 * if a 4D data set. */ int segy_offsets( segy_file*, int il, int xl, int traces, int* out, long trace0, int trace_bsize ); /* * The names of the individual offsets. `out` must be a buffer of * `segy_offsets` elements. */ int segy_offset_indices( segy_file*, int offset_field, int offsets, int* out, long trace0, int trace_bsize ); /* * read/write traces. Does not manipulate the buffers at all, i.e. in order to * make sense of the read trace it must be converted to native floats, and the * buffer sent to write must be converted to target float. */ int segy_readtrace( segy_file*, int traceno, float* buf, long trace0, int trace_bsize ); int segy_writetrace( segy_file*, int traceno, const float* buf, long trace0, int trace_bsize ); /* * read/write sub traces, with the same assumption and requirements as * segy_readtrace. start and stop are *indices*, not byte offsets, so * segy_readsubtr(fp, traceno, 10, 12, ...) reads samples 10 through 12, and * not bytes 10 through 12. * * start and stop are in the range [start,stop), so start=0, stop=5, step=2 * yields [0, 2, 4], whereas stop=4 yields [0, 2] * * When step is negative, the subtrace will be read in reverse. If step is * negative and [0,n) is desired, pass use -1 for stop. Other negative values * are undefined. If the range [n, m) where m is larger than the samples is * considered undefined. Any [n, m) where distance(n,m) > samples is undefined. * * The parameter rangebuf is a pointer to a buffer of at least abs(stop-start) * size. This is largely intended for script-C boundaries. In code paths where * step is not 1 or -1, and mmap is not activated, these functions will * *allocate* a buffer to read data from file in chunks. This is a significant * speedup over multiple fread calls, at the cost of a clunkier interface. This * is a tradeoff, since this function is often called in an inner loop. If * you're fine with these functions allocating and freeing this buffer for you, * rangebuf can be NULL. */ int segy_readsubtr( segy_file*, int traceno, int start, int stop, int step, float* buf, float* rangebuf, long trace0, int trace_bsize ); int segy_writesubtr( segy_file*, int traceno, int start, int stop, int step, const float* buf, float* rangebuf, long trace0, int trace_bsize ); /* * convert to/from native float from segy formats (likely IBM or IEEE). Size * parameter is long long because it needs to know the number of *samples*, * which can be very large for bulk conversion of a collection of traces. */ int segy_to_native( int format, long long size, float* buf ); int segy_from_native( int format, long long size, float* buf ); int segy_read_line( segy_file* fp, int line_trace0, int line_length, int stride, int offsets, float* buf, long trace0, int trace_bsize ); int segy_write_line( segy_file* fp, int line_trace0, int line_length, int stride, int offsets, const float* buf, long trace0, int trace_bsize ); /* * Count inlines and crosslines. Use this function to determine how large buffer * the functions `segy_inline_indices` and `segy_crossline_indices` expect. If * the file is sorted on inlines, `field` should the trace header field for the * crossline number, and the inline number if the file is sorted on crosslines. * If the file is sorted on inlines, `l1out` will contain the number of * inlines, and `l2out` crosslines, and the other way around if the file is * sorted on crosslines. * * `offsets` is the number of offsets in the file and be found with * `segy_offsets`. * * If the file has only 1 trace (or, for pre-stack files, 1-trace-per-offset), * segyio considers this as 1 line in each direction. */ int segy_count_lines( segy_file*, int field, int offsets, int* l1out, int* l2out, long trace0, int trace_bsize ); /* * Alternative interface for segy_count_lines. If you have information about * sorting this is easier to use, but requires both the inline and crossline * header field positions. Does the argument shuffling needed to call * segy_count_lines. */ int segy_lines_count( segy_file*, int il, int xl, int sorting, int offsets, int* il_count, int* xl_count, long trace0, int trace_bsize ); /* * Find the `line_length` for the inlines. Assumes all inlines, crosslines and * traces don't vary in length. * * `inline_count` and `crossline_count` are the two values obtained with * `segy_count_lines`. * * These functions cannot fail and return the length, not an error code. */ int segy_inline_length(int crossline_count); int segy_crossline_length(int inline_count); /* * Find the indices of the inlines and write to `buf`. `offsets` are the number * of offsets for this file as returned by `segy_offsets` */ int segy_inline_indices( segy_file*, int il, int sorting, int inline_count, int crossline_count, int offsets, int* buf, long trace0, int trace_bsize ); int segy_crossline_indices( segy_file*, int xl, int sorting, int inline_count, int crossline_count, int offsets, int* buf, long trace0, int trace_bsize ); /* * Find the first `traceno` of the line `lineno`. `linenos` should be the line * indices returned by `segy_inline_indices` or `segy_crossline_indices`. The * stride depends on the sorting and is given by `segy_inline_stride` or * `segy_crossline_stride`. `offsets` is given by `segy_offsets` function, and * is the number of offsets in this file (1 for post stack data). `line_length` * is the length, i.e. traces per line, given by `segy_inline_length` or * `segy_crossline_length`. * * To read/write an inline, read `line_length` starting at `traceno`, * incrementing `traceno` with `stride` `line_length` times. */ int segy_line_trace0( int lineno, int line_length, int stride, int offsets, const int* linenos, int linenos_sz, int* traceno ); /* * Find the `rotation` of the survey in radians. * * Returns the clock-wise rotation around north, i.e. the angle between the * first line given and north axis. In this context, north is the direction * that yields a higher CDP-Y coordinate, and east is the direction that yields * a higher CDP-X coordinate. * * N * | * | * | + * | |~~/``````/ * | | /------/ * | |/,,,,,,/ * | * +--------------- E * * * When the survey is as depicted, and the first line is starting in the * south-west corner and goes north, the angle (~~) is < pi/4. If the first * line is parallel with equator moving east, the angle is pi/2. * * The return value is in the domain [0, 2pi) */ int segy_rotation_cw( segy_file*, int line_length, int stride, int offsets, const int* linenos, int linenos_sz, float* rotation, long trace0, int trace_bsize ); /* * Find the stride needed for an inline/crossline traversal. */ int segy_inline_stride( int sorting, int inline_count, int* stride ); int segy_crossline_stride( int sorting, int crossline_count, int* stride ); typedef enum { SEGY_TR_SEQ_LINE = 1, SEGY_TR_SEQ_FILE = 5, SEGY_TR_FIELD_RECORD = 9, SEGY_TR_NUMBER_ORIG_FIELD = 13, SEGY_TR_ENERGY_SOURCE_POINT = 17, SEGY_TR_ENSEMBLE = 21, SEGY_TR_NUM_IN_ENSEMBLE = 25, SEGY_TR_TRACE_ID = 29, SEGY_TR_SUMMED_TRACES = 31, SEGY_TR_STACKED_TRACES = 33, SEGY_TR_DATA_USE = 35, SEGY_TR_OFFSET = 37, SEGY_TR_RECV_GROUP_ELEV = 41, SEGY_TR_SOURCE_SURF_ELEV = 45, SEGY_TR_SOURCE_DEPTH = 49, SEGY_TR_RECV_DATUM_ELEV = 53, SEGY_TR_SOURCE_DATUM_ELEV = 57, SEGY_TR_SOURCE_WATER_DEPTH = 61, SEGY_TR_GROUP_WATER_DEPTH = 65, SEGY_TR_ELEV_SCALAR = 69, SEGY_TR_SOURCE_GROUP_SCALAR = 71, SEGY_TR_SOURCE_X = 73, SEGY_TR_SOURCE_Y = 77, SEGY_TR_GROUP_X = 81, SEGY_TR_GROUP_Y = 85, SEGY_TR_COORD_UNITS = 89, SEGY_TR_WEATHERING_VELO = 91, SEGY_TR_SUBWEATHERING_VELO = 93, SEGY_TR_SOURCE_UPHOLE_TIME = 95, SEGY_TR_GROUP_UPHOLE_TIME = 97, SEGY_TR_SOURCE_STATIC_CORR = 99, SEGY_TR_GROUP_STATIC_CORR = 101, SEGY_TR_TOT_STATIC_APPLIED = 103, SEGY_TR_LAG_A = 105, SEGY_TR_LAG_B = 107, SEGY_TR_DELAY_REC_TIME = 109, SEGY_TR_MUTE_TIME_START = 111, SEGY_TR_MUTE_TIME_END = 113, SEGY_TR_SAMPLE_COUNT = 115, SEGY_TR_SAMPLE_INTER = 117, SEGY_TR_GAIN_TYPE = 119, SEGY_TR_INSTR_GAIN_CONST = 121, SEGY_TR_INSTR_INIT_GAIN = 123, SEGY_TR_CORRELATED = 125, SEGY_TR_SWEEP_FREQ_START = 127, SEGY_TR_SWEEP_FREQ_END = 129, SEGY_TR_SWEEP_LENGTH = 131, SEGY_TR_SWEEP_TYPE = 133, SEGY_TR_SWEEP_TAPERLEN_START = 135, SEGY_TR_SWEEP_TAPERLEN_END = 137, SEGY_TR_TAPER_TYPE = 139, SEGY_TR_ALIAS_FILT_FREQ = 141, SEGY_TR_ALIAS_FILT_SLOPE = 143, SEGY_TR_NOTCH_FILT_FREQ = 145, SEGY_TR_NOTCH_FILT_SLOPE = 147, SEGY_TR_LOW_CUT_FREQ = 149, SEGY_TR_HIGH_CUT_FREQ = 151, SEGY_TR_LOW_CUT_SLOPE = 153, SEGY_TR_HIGH_CUT_SLOPE = 155, SEGY_TR_YEAR_DATA_REC = 157, SEGY_TR_DAY_OF_YEAR = 159, SEGY_TR_HOUR_OF_DAY = 161, SEGY_TR_MIN_OF_HOUR = 163, SEGY_TR_SEC_OF_MIN = 165, SEGY_TR_TIME_BASE_CODE = 167, SEGY_TR_WEIGHTING_FAC = 169, SEGY_TR_GEOPHONE_GROUP_ROLL1 = 171, SEGY_TR_GEOPHONE_GROUP_FIRST = 173, SEGY_TR_GEOPHONE_GROUP_LAST = 175, SEGY_TR_GAP_SIZE = 177, SEGY_TR_OVER_TRAVEL = 179, SEGY_TR_CDP_X = 181, SEGY_TR_CDP_Y = 185, SEGY_TR_INLINE = 189, SEGY_TR_CROSSLINE = 193, SEGY_TR_SHOT_POINT = 197, SEGY_TR_SHOT_POINT_SCALAR = 201, SEGY_TR_MEASURE_UNIT = 203, SEGY_TR_TRANSDUCTION_MANT = 205, SEGY_TR_TRANSDUCTION_EXP = 209, SEGY_TR_TRANSDUCTION_UNIT = 211, SEGY_TR_DEVICE_ID = 213, SEGY_TR_SCALAR_TRACE_HEADER = 215, SEGY_TR_SOURCE_TYPE = 217, SEGY_TR_SOURCE_ENERGY_DIR_MANT = 219, SEGY_TR_SOURCE_ENERGY_DIR_EXP = 223, SEGY_TR_SOURCE_MEASURE_MANT = 225, SEGY_TR_SOURCE_MEASURE_EXP = 229, SEGY_TR_SOURCE_MEASURE_UNIT = 231, SEGY_TR_UNASSIGNED1 = 233, SEGY_TR_UNASSIGNED2 = 237 } SEGY_FIELD; typedef enum { SEGY_BIN_JOB_ID = 3201, SEGY_BIN_LINE_NUMBER = 3205, SEGY_BIN_REEL_NUMBER = 3209, SEGY_BIN_TRACES = 3213, SEGY_BIN_AUX_TRACES = 3215, SEGY_BIN_INTERVAL = 3217, SEGY_BIN_INTERVAL_ORIG = 3219, SEGY_BIN_SAMPLES = 3221, SEGY_BIN_SAMPLES_ORIG = 3223, SEGY_BIN_FORMAT = 3225, SEGY_BIN_ENSEMBLE_FOLD = 3227, SEGY_BIN_SORTING_CODE = 3229, SEGY_BIN_VERTICAL_SUM = 3231, SEGY_BIN_SWEEP_FREQ_START = 3233, SEGY_BIN_SWEEP_FREQ_END = 3235, SEGY_BIN_SWEEP_LENGTH = 3237, SEGY_BIN_SWEEP = 3239, SEGY_BIN_SWEEP_CHANNEL = 3241, SEGY_BIN_SWEEP_TAPER_START = 3243, SEGY_BIN_SWEEP_TAPER_END = 3245, SEGY_BIN_TAPER = 3247, SEGY_BIN_CORRELATED_TRACES = 3249, SEGY_BIN_BIN_GAIN_RECOVERY = 3251, SEGY_BIN_AMPLITUDE_RECOVERY = 3253, SEGY_BIN_MEASUREMENT_SYSTEM = 3255, SEGY_BIN_IMPULSE_POLARITY = 3257, SEGY_BIN_VIBRATORY_POLARITY = 3259, SEGY_BIN_UNASSIGNED1 = 3261, SEGY_BIN_SEGY_REVISION = 3501, SEGY_BIN_TRACE_FLAG = 3503, SEGY_BIN_EXT_HEADERS = 3505, SEGY_BIN_UNASSIGNED2 = 3507, } SEGY_BINFIELD; typedef enum { SEGY_IBM_FLOAT_4_BYTE = 1, SEGY_SIGNED_INTEGER_4_BYTE = 2, SEGY_SIGNED_SHORT_2_BYTE = 3, SEGY_FIXED_POINT_WITH_GAIN_4_BYTE = 4, // Obsolete SEGY_IEEE_FLOAT_4_BYTE = 5, SEGY_NOT_IN_USE_1 = 6, SEGY_NOT_IN_USE_2 = 7, SEGY_SIGNED_CHAR_1_BYTE = 8 } SEGY_FORMAT; typedef enum { SEGY_UNKNOWN_SORTING = 0, SEGY_CROSSLINE_SORTING = 1, SEGY_INLINE_SORTING = 2, } SEGY_SORTING; typedef enum { SEGY_OK = 0, SEGY_FOPEN_ERROR, SEGY_FSEEK_ERROR, SEGY_FREAD_ERROR, SEGY_FWRITE_ERROR, SEGY_INVALID_FIELD, SEGY_INVALID_SORTING, SEGY_MISSING_LINE_INDEX, SEGY_INVALID_OFFSETS, SEGY_TRACE_SIZE_MISMATCH, SEGY_INVALID_ARGS, SEGY_MMAP_ERROR, SEGY_MMAP_INVALID, SEGY_READONLY, SEGY_NOTFOUND, } SEGY_ERROR; #ifdef __cplusplus } #endif // __cplusplus #endif //SEGYIO_SEGY_H segyio-1.5.2/lib/test/0000775000372000037200000000000013253720451014147 5ustar travistravissegyio-1.5.2/lib/test/testsuite.cpp0000664000372000037200000000006513253720451016705 0ustar travistravis#define CATCH_CONFIG_MAIN #include segyio-1.5.2/lib/test/segy.cpp0000664000372000037200000007243613253720451015636 0ustar travistravis#include #include #include #include #include #include #include #ifndef MMAP_TAG #define MMAP_TAG "" #endif namespace { struct slice { int start, stop, step; }; std::string str( const slice& s ) { return "(" + std::to_string( s.start ) + "," + std::to_string( s.stop ) + "," + std::to_string( s.step ) + ")"; } class ApproxRange : public Catch::MatcherBase< std::vector< float > > { public: explicit ApproxRange( const std::vector< float >& xs ) : lhs( xs ) {} virtual bool match( const std::vector< float >& xs ) const override { if( xs.size() != lhs.size() ) return false; for( size_t i = 0; i < xs.size(); ++i ) if( xs[ i ] != Approx(this->lhs[ i ]) ) return false; return true; } virtual std::string describe() const override { using str = Catch::StringMaker< std::vector< float > >; return "~= " + str::convert( this->lhs ); } private: std::vector< float > lhs; }; struct Err { // cppcheck-suppress noExplicitConstructor Err( int err ) : err( err ) {} bool operator == ( Err other ) const { return this->err == other.err; } bool operator != ( Err other ) const { return !(*this == other); } static Err ok() { return SEGY_OK; } static Err args() { return SEGY_INVALID_ARGS; } static Err field() { return SEGY_INVALID_FIELD; } int err; }; } namespace Catch { template<> struct StringMaker< Err > { static std::string convert( const Err& err ) { switch( err.err ) { case SEGY_OK : return "OK"; case SEGY_FOPEN_ERROR: return "SEGY_FOPEN_ERROR"; case SEGY_FSEEK_ERROR: return "SEGY_FSEEK_ERROR"; case SEGY_FREAD_ERROR: return "SEGY_FREAD_ERROR"; case SEGY_FWRITE_ERROR: return "SEGY_FWRITE_ERROR"; case SEGY_INVALID_FIELD: return "SEGY_INVALID_FIELD"; case SEGY_INVALID_SORTING: return "SEGY_INVALID_SORTING"; case SEGY_MISSING_LINE_INDEX: return "SEGY_MISSING_LINE_INDEX"; case SEGY_INVALID_OFFSETS: return "SEGY_INVALID_OFFSETS"; case SEGY_TRACE_SIZE_MISMATCH: return "SEGY_TRACE_SIZE_MISMATCH"; case SEGY_INVALID_ARGS: return "SEGY_INVALID_ARGS"; case SEGY_MMAP_ERROR: return "SEGY_MMAP_ERROR"; case SEGY_MMAP_INVALID: return "SEGY_MMAP_INVALID"; } return "Unknown error"; } }; } SCENARIO( MMAP_TAG "reading a file", "[c.segy]" MMAP_TAG ) { const char* file = "test-data/small.sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file, "rb" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); if( MMAP_TAG != std::string("") ) REQUIRE( Err( segy_mmap( fp ) ) == Err::ok() ); WHEN( "finding traces initial byte offset and sizes" ) { char header[ SEGY_BINARY_HEADER_SIZE ]; REQUIRE( Err( segy_binheader( fp, header ) ) == Err::ok() ); int samples = segy_samples( header ); long trace0 = segy_trace0( header ); int trace_bsize = segy_trace_bsize( samples ); CHECK( trace0 == 3600 ); CHECK( samples == 50 ); CHECK( trace_bsize == 50 * 4 ); } const long trace0 = 3600; const int trace_bsize = 50 * 4; const int samples = 50; WHEN( "determining number of traces" ) { int traces = 0; Err err = segy_traces( fp, &traces, trace0, trace_bsize ); REQUIRE( err == Err::ok() ); CHECK( traces == 25 ); GIVEN( "trace0 outside its domain" ) { WHEN( "trace0 is after end-of-file" ) { err = segy_traces( fp, &traces, 50000, trace_bsize ); THEN( "segy_traces fail" ) CHECK( err == Err::args() ); THEN( "the input does not change" ) CHECK( traces == 25 ); } WHEN( "trace0 is negative" ) { err = segy_traces( fp, &traces, -1, trace_bsize ); THEN( "segy_traces fail" ) CHECK( err == Err::args() ); THEN( "the input does not change" ) CHECK( traces == 25 ); } } } const int traces = 25; const int il = SEGY_TR_INLINE; const int xl = SEGY_TR_CROSSLINE; const int of = SEGY_TR_OFFSET; GIVEN( "an inline sorted file" ) { THEN( "inline sorting is inferred" ) { int sorting = -1; const Err err = segy_sorting( fp, il, xl, of, &sorting, trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK( sorting == SEGY_INLINE_SORTING ); } WHEN( "swapping inline and crossline position" ) { THEN( "crossline sorting is inferred" ) { int sorting = -1; const Err err = segy_sorting( fp, xl, il, of, &sorting, trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK( sorting == SEGY_CROSSLINE_SORTING ); } } } const int sorting = SEGY_INLINE_SORTING; GIVEN( "a post stack file" ) { int offsets = -1; const Err err = segy_offsets( fp, il, xl, traces, &offsets, trace0, trace_bsize ); THEN( "there is only one offset" ) { CHECK( err == Err::ok() ); CHECK( offsets == 1 ); } WHEN( "swapping inline and crossline position" ) { int offsets = -1; const Err err = segy_offsets( fp, xl, il, traces, &offsets, trace0, trace_bsize ); THEN( "there is only one offset" ) { CHECK( err == Err::ok() ); CHECK( offsets == 1 ); } } } const int offsets = 1; WHEN( "determining offset labels" ) { int offset_index = -1; const Err err = segy_offset_indices( fp, of, offsets, &offset_index, trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK( offset_index == 1 ); } WHEN( "counting lines" ) { int ilsz = -1; int xlsz = -1; WHEN( "using segy_count_lines" ) { const Err err = segy_count_lines( fp, xl, offsets, &ilsz, &xlsz, trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK( ilsz == 5 ); CHECK( xlsz == 5 ); } WHEN( "using segy_lines_count" ) { const Err err = segy_lines_count( fp, il, xl, sorting, offsets, &ilsz, &xlsz, trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK( ilsz == 5 ); CHECK( xlsz == 5 ); } } const int inlines_sizes = 5; const int crosslines_sizes = 5; const int format = SEGY_IBM_FLOAT_4_BYTE; WHEN( "inferring inline structure" ) { const std::vector< int > indices = { 1, 2, 3, 4, 5 }; CHECK( segy_inline_length( crosslines_sizes ) == 5 ); WHEN( "finding inline numbers" ) { std::vector< int > result( inlines_sizes ); const Err err = segy_inline_indices( fp, il, sorting, inlines_sizes, crosslines_sizes, offsets, result.data(), trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK_THAT( result, Catch::Equals( indices ) ); } WHEN( "determining inline 4's first trace number" ) { GIVEN( "an inline sorted file" ) THEN( "the stride is 1" ) { int stride = -1; Err err = segy_inline_stride( sorting, inlines_sizes, &stride ); CHECK( err == Err::ok() ); CHECK( stride == 1 ); } const int stride = 1; int line_trace0 = -1; const Err err = segy_line_trace0( 4, crosslines_sizes, stride, offsets, indices.data(), inlines_sizes, &line_trace0 ); CHECK( err == Err::ok() ); CHECK( line_trace0 == 15 ); } } WHEN( "reading a trace header" ) { char buf[ SEGY_TRACE_HEADER_SIZE ] = {}; GIVEN( "a valid field" ) { Err err = segy_traceheader( fp, 0, buf, trace0, trace_bsize ); CHECK( err == Err::ok() ); int ilno = 0; err = segy_get_field( buf, SEGY_TR_INLINE, &ilno ); CHECK( err == Err::ok() ); CHECK( ilno == 1 ); } GIVEN( "an invalid field" ) { int x = -1; Err err = segy_get_field( buf, SEGY_TRACE_HEADER_SIZE + 10, &x ); CHECK( err == Err::field() ); CHECK( x == -1 ); err = segy_get_field( buf, SEGY_TR_INLINE + 1, &x ); CHECK( err == Err::field() ); CHECK( x == -1 ); } } WHEN( "inferring crossline structure" ) { const std::vector< int > indices = { 20, 21, 22, 23, 24 }; CHECK( segy_crossline_length( inlines_sizes ) == 5 ); WHEN( "finding crossline numbers" ) { std::vector< int > result( crosslines_sizes ); const Err err = segy_crossline_indices( fp, xl, sorting, inlines_sizes, crosslines_sizes, offsets, result.data(), trace0, trace_bsize ); CHECK( err == Err::ok() ); CHECK_THAT( result, Catch::Equals( indices ) ); } WHEN( "determining crossline 22's first trace number" ) { GIVEN( "an inline sorted file" ) THEN( "the stride is 5" ) { int stride = -1; Err err = segy_crossline_stride( sorting, crosslines_sizes, &stride ); CHECK( err == Err::ok() ); CHECK( stride == 5 ); } const int stride = 5; int line_trace0 = -1; const Err err = segy_line_trace0( 22, inlines_sizes, stride, offsets, indices.data(), crosslines_sizes, &line_trace0 ); CHECK( err == Err::ok() ); CHECK( line_trace0 == 2 ); } } WHEN( "reading a subtrace" ) { const std::vector< slice > inputs = { { 3, 19, 5 }, { 18, 2, -5 }, { 3, -1, -1 }, { 24, -1, -5 } }; const std::vector< std::vector< float > > expect = { { 3.20003f, 3.20008f, 3.20013f, 3.20018f }, { 3.20018f, 3.20013f, 3.20008f, 3.20003f }, { 3.20003f, 3.20002f, 3.20001f, 3.20000f }, { 3.20024f, 3.20019f, 3.20014f, 3.20009f, 3.20004f } }; for( size_t i = 0; i < inputs.size(); ++i ) { WHEN( "slice is " + str( inputs[ i ] ) ) { std::vector< float > buf( expect[ i ].size() ); auto start = inputs[ i ].start; auto stop = inputs[ i ].stop; auto step = inputs[ i ].step; Err err = segy_readsubtr( fp, 10, start, stop, step, buf.data(), nullptr, trace0, trace_bsize ); segy_to_native( format, buf.size(), buf.data() ); CHECK( err == Err::ok() ); CHECK_THAT( buf, ApproxRange( expect[ i ] ) ); } } } const std::vector< int > inlines = { 1, 2, 3, 4, 5 }; const std::vector< int > crosslines = { 20, 21, 22, 23, 24 }; const int inline_length = 5; const int crossline_length = 5; WHEN( "reading an inline" ) { for( const auto il : inlines ) { std::vector< float > line( inline_length * samples ); const int stride = 1; int line_trace0 = -1; Err err = segy_line_trace0( il, inline_length, stride, offsets, inlines.data(), inlines.size(), &line_trace0 ); REQUIRE( err == Err::ok() ); REQUIRE( line_trace0 != -1 ); err = segy_read_line( fp, line_trace0, inlines_sizes, stride, offsets, line.data(), trace0, trace_bsize ); REQUIRE( err == Err::ok() ); segy_to_native( format, line.size(), line.data() ); for( const auto xl : crosslines ) { auto point = std::to_string(il) + ", " + std::to_string(xl); THEN( "at intersection (" + point + ")" ) { const auto i = (xl - 20) * samples; for( size_t s = 0; s < samples; ++s ) { const float expected = il + (0.01 * xl) + (1e-5 * s); CHECK( line.at(i+s) == Approx( expected ) ); } } } } } WHEN( "reading a crossline" ) { for( const auto xl : crosslines ) { std::vector< float > line( crossline_length * samples ); const int stride = 5; int line_trace0 = -1; Err err = segy_line_trace0( xl, crosslines_sizes, stride, offsets, crosslines.data(), crosslines.size(), &line_trace0 ); REQUIRE( err == Err::ok() ); REQUIRE( line_trace0 != -1 ); err = segy_read_line( fp, line_trace0, crosslines_sizes, stride, offsets, line.data(), trace0, trace_bsize ); REQUIRE( err == Err::ok() ); segy_to_native( format, line.size(), line.data() ); for( const auto il : inlines ) { auto point = std::to_string(il) + ", " + std::to_string(xl); THEN( "at intersection (" + point + ")" ) { const auto i = (il - 1) * samples; for( size_t s = 0; s < samples; ++s ) { const float expected = il + (0.01 * xl) + (1e-5 * s); CHECK( line.at(i+s) == Approx( expected ) ); } } } } } } SCENARIO( MMAP_TAG "writing to a file", "[c.segy]" MMAP_TAG ) { const long trace0 = 3600; const int trace_bsize = 50 * 4; const float dummy[ 50 ] = {}; WHEN( "writing parts of a trace" ) { const int format = SEGY_IBM_FLOAT_4_BYTE; const std::vector< slice > inputs = { { 3, 19, 5 }, { 18, 2, -5 }, { 3, -1, -1 }, { 24, -1, -5 } }; const std::vector< std::vector< float > > expect = { { 3.20003f, 3.20008f, 3.20013f, 3.20018f }, { 3.20018f, 3.20013f, 3.20008f, 3.20003f }, { 3.20003f, 3.20002f, 3.20001f, 3.20000f }, { 3.20024f, 3.20019f, 3.20014f, 3.20009f, 3.20004f } }; for( size_t i = 0; i < inputs.size(); ++i ) { WHEN( "slice is " + str( inputs[ i ] ) ) { const auto file = "wsubtr" MMAP_TAG + str(inputs[i]) + ".sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file.c_str(), "w+b" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); Err err = segy_writetrace( fp, 10, dummy, trace0, trace_bsize ); REQUIRE( err == Err::ok() ); if( MMAP_TAG != std::string("") ) REQUIRE( Err( segy_mmap( fp ) ) == Err::ok() ); std::vector< float > buf( expect[ i ].size() ); auto start = inputs[ i ].start; auto stop = inputs[ i ].stop; auto step = inputs[ i ].step; auto out = expect[ i ]; segy_from_native( format, out.size(), out.data() ); err = segy_writesubtr( fp, i, start, stop, step, out.data(), nullptr, trace0, trace_bsize ); CHECK( err == Err::ok() ); THEN( "updates are observable" ) { err = segy_readsubtr( fp, i, start, stop, step, buf.data(), nullptr, trace0, trace_bsize ); segy_to_native( format, buf.size(), buf.data() ); CHECK( err == Err::ok() ); CHECK_THAT( buf, ApproxRange( expect[ i ] ) ); } } } } } SCENARIO( MMAP_TAG "extracting header fields", "[c.segy]" MMAP_TAG ) { const char* file = "test-data/small.sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file, "rb" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); const int trace0 = 3600; const int trace_bsize = 50 * 4; if( MMAP_TAG != std::string("") ) REQUIRE( Err( segy_mmap( fp ) ) == Err::ok() ); WHEN( "reading inline labels" ) { const std::vector< int > inlines = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, }; std::vector< int > buf( inlines.size() ); const slice input = { 0, 25, 1 }; const auto start = input.start; const auto stop = input.stop; const auto step = input.step; const Err err = segy_field_forall( fp, SEGY_TR_INLINE, start, stop, step, buf.data(), trace0, trace_bsize ); CHECK( err == Err::ok() ); WHEN( "in range " + str( input ) ) CHECK_THAT( buf, Catch::Equals( inlines ) ); } WHEN( "reading crossline labels" ) { const std::vector< std::pair< slice, std::vector< int > > > pairs = { { { 0, 25, 1 }, { 20, 21, 22, 23, 24, 20, 21, 22, 23, 24, 20, 21, 22, 23, 24, 20, 21, 22, 23, 24, 20, 21, 22, 23, 24, }, }, { { 1, 25, 3 }, { 21, 24, 22, 20, 23, 21, 24, 22, }, }, { { 22, 0, -3 }, { 22, 24, 21, 23, 20, 22, 24, 21 }, }, { { 24, -1, -5 }, { 24, 24, 24, 24, 24 }, }, }; for( const auto& p : pairs ) { const auto& input = p.first; const auto& xl = p.second; std::vector< int > buf( xl.size() ); const auto start = input.start; const auto stop = input.stop; const auto step = input.step; const Err err = segy_field_forall( fp, SEGY_TR_CROSSLINE, start, stop, step, buf.data(), trace0, trace_bsize ); CHECK( err == Err::ok() ); WHEN( "in range " + str( input ) ) CHECK_THAT( buf, Catch::Equals( xl ) ); } } } SCENARIO( MMAP_TAG "modifying trace header", "[c.segy]" MMAP_TAG ) { const int samples = 10; const int trace_bsize = segy_trace_bsize( samples ); const int trace0 = 0; const float emptytr[ samples ] = {}; const char emptyhdr[ SEGY_TRACE_HEADER_SIZE ] = {}; WHEN( "writing iline no" ) { char header[ SEGY_TRACE_HEADER_SIZE ] = {}; GIVEN( "an invalid field" ) { Err err = segy_set_field( header, SEGY_TR_INLINE + 1, 2 ); CHECK( err == Err::field() ); } Err err = segy_set_field( header, SEGY_TR_INLINE, 2 ); CHECK( err == Err::ok() ); err = segy_set_field( header, SEGY_TR_SOURCE_GROUP_SCALAR, -100 ); CHECK( err == Err::ok() ); THEN( "the header buffer is updated") { int ilno = 0; int scale = 0; err = segy_get_field( header, SEGY_TR_INLINE, &ilno ); CHECK( err == Err::ok() ); err = segy_get_field( header, SEGY_TR_SOURCE_GROUP_SCALAR, &scale ); CHECK( err == Err::ok() ); CHECK( ilno == 2 ); CHECK( scale == -100 ); } const char* file = MMAP_TAG "write-traceheader.sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file, "w+b" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); /* make a file and write to last trace (to accurately get size) */ err = segy_write_traceheader( fp, 10, emptyhdr, trace0, trace_bsize ); REQUIRE( err == Err::ok() ); err = segy_writetrace( fp, 10, emptytr, trace0, trace_bsize ); REQUIRE( err == Err::ok() ); if( MMAP_TAG != std::string("") ) REQUIRE( Err( segy_mmap( fp ) ) == Err::ok() ); err = segy_write_traceheader( fp, 5, header, trace0, trace_bsize ); CHECK( err == Err::ok() ); THEN( "changes are observable on disk" ) { char header[ SEGY_TRACE_HEADER_SIZE ] = {}; int ilno = 0; int scale = 0; err = segy_traceheader( fp, 5, header, trace0, trace_bsize ); CHECK( err == Err::ok() ); err = segy_get_field( header, SEGY_TR_INLINE, &ilno ); CHECK( err == Err::ok() ); err = segy_get_field( header, SEGY_TR_SOURCE_GROUP_SCALAR, &scale ); CHECK( err == Err::ok() ); CHECK( ilno == 2 ); CHECK( scale == -100 ); } } } SCENARIO( MMAP_TAG "reading text header", "[c.segy]" MMAP_TAG ) { const std::string expected = "C 1 DATE: 22/02/2016 " "C 2 AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE " "C 3 FIRST SAMPLE: 4 MS, LAST SAMPLE: 1400 MS, SAMPLE INTERVAL: 4 MS " "C 4 DATA RANGE: INLINES=(2479-2500) (INC 1),CROSSLINES=(1428-1440) (INC 1) " "C 5 PROCESSING GRID CORNERS: " "C 6 DISTANCE BETWEEN INLINES: 2499.75 M, CROSSLINES: 1250 M " "C 7 1: INLINE 2479, CROSSLINE 1428, UTM-X 9976386.00, UTM-Y 9989096.00 " "C 8 2: INLINE 2479, CROSSLINE 1440, UTM-X 9983886.00, UTM-Y 10002087.00 " "C 9 3: INLINE 2500, CROSSLINE 1428, UTM-X 10021847.00, UTM-Y 9962849.00 " "C10 4: INLINE 2500, CROSSLINE 1440, UTM-X 10029348.00, UTM-Y 9975839.00 " "C11 TRACE HEADER POSITION: " "C12 INLINE BYTES 005-008 | OFFSET BYTES 037-040 " "C13 CROSSLINE BYTES 021-024 | CMP UTM-X BYTES 181-184 " "C14 CMP UTM-Y BYTES 185-188 " "C15 END EBCDIC HEADER " "C16 " "C17 " "C18 " "C19 " "C20 " "C21 " "C22 " "C23 " "C24 " "C25 " "C26 " "C27 " "C28 " "C29 " "C30 " "C31 " "C32 " "C33 " "C34 " "C35 " "C36 " "C37 " "C38 " "C39 " "C40 \x80"; const char* file = "test-data/text.sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file, "rb" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); if( MMAP_TAG != std::string("") ) REQUIRE( Err( segy_mmap( fp ) ) == Err::ok() ); char ascii[ SEGY_TEXT_HEADER_SIZE + 1 ] = {}; const Err err = segy_read_textheader( fp, ascii ); CHECK( err == Err::ok() ); CHECK( ascii == expected ); } SCENARIO( MMAP_TAG "reading a large file", "[c.segy]" MMAP_TAG ) { GIVEN( "a large file" ) { const char* file = MMAP_TAG "4G-file.sgy"; std::unique_ptr< segy_file, decltype( &segy_close ) > ufp{ segy_open( file, "w+b" ), &segy_close }; REQUIRE( ufp ); auto fp = ufp.get(); const int trace = 5000000; const int trace_bsize = 1000; const long long tracesize = trace_bsize + SEGY_TRACE_HEADER_SIZE; const long trace0 = 0; const Err err = segy_seek( fp, trace, trace0, trace_bsize ); CHECK( err == Err::ok() ); WHEN( "reading past 4GB (pos >32bit)" ) { THEN( "there is no overflow" ) { const long long pos = segy_ftell( fp ); CHECK( pos > std::numeric_limits< int >::max() ); CHECK( pos != -1 ); CHECK( pos == trace * tracesize ); } } } } segyio-1.5.2/lib/test/mmap.cpp0000664000372000037200000000012413253720451015602 0ustar travistravis#ifdef HAVE_MMAP #define MMAP_TAG "[mmap]" #include "segy.cpp" #endif //HAVE_MMAP segyio-1.5.2/lib/CMakeLists.txt0000664000372000037200000000562013253720451015733 0ustar travistravisproject(libsegyio C) set(SOURCE_FILES src/segy.c) set(STATIC_NAME segyio) if(MSVC) # MSVC outputs the same name for static and shared libraries (with the same # extension), so we need to differentiate between the two somehow. set(STATIC_NAME "${STATIC_NAME}-static") set(DLL_EXPORT_FILES src/segy.def) endif() # # static build # add_library(segyio-static STATIC ${SOURCE_FILES}) target_link_libraries(segyio-static ${m} ${ws2}) target_compile_definitions(segyio-static PRIVATE ${htons} ${mmap} ${fstat} ${ftello}) target_compile_options(segyio-static BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) set_target_properties(segyio-static PROPERTIES OUTPUT_NAME ${STATIC_NAME} CLEAN_DIRECT_OUTPUT 1) target_include_directories( segyio-static PUBLIC $ $ PRIVATE src include) # # dynamic build # add_library(segyio-shared SHARED ${SOURCE_FILES} ${DLL_EXPORT_FILES}) target_link_libraries(segyio-shared ${m} ${ws2}) target_compile_options(segyio-shared BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) target_compile_definitions(segyio-shared PRIVATE ${htons} ${mmap} ${fstat} ${ftello}) set_target_properties(segyio-shared PROPERTIES SOVERSION ${segyio_MAJOR} VERSION ${segyio_MAJOR} OUTPUT_NAME segyio CLEAN_DIRECT_OUTPUT 1) target_include_directories( segyio-shared PUBLIC $ $ PRIVATE src include) if (BUILD_SHARED_LIBS) add_library(segyio ALIAS segyio-shared) else () add_library(segyio ALIAS segyio-static) set_property(TARGET segyio-static PROPERTY POSITION_INDEPENDENT_CODE ON) endif () # # install & export # install(TARGETS segyio-static segyio-shared EXPORT segyio ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY include/ DESTINATION include) install(EXPORT segyio DESTINATION share/segyio/cmake FILE segyio-config.cmake) export(TARGETS segyio-static segyio-shared FILE segyio-config.cmake) if (NOT BUILD_TESTING) return () endif () configure_file(${testdata}/small.sgy test-data/small.sgy COPYONLY) configure_file(${testdata}/text.sgy test-data/text.sgy COPYONLY) add_executable(c.segy test/testsuite.cpp test/segy.cpp test/mmap.cpp) target_include_directories(c.segy PRIVATE src) target_link_libraries(c.segy catch2 segyio) target_compile_options(c.segy BEFORE PRIVATE ${c++11} $<$:${warnings-c}> ${mmap}) add_test(NAME c.segy COMMAND c.segy ~[mmap] [c.segy]) add_test(NAME c.segy.mmap COMMAND c.segy [mmap]) segyio-1.5.2/lib/src/0000775000372000037200000000000013253720451013757 5ustar travistravissegyio-1.5.2/lib/src/segy.def0000664000372000037200000000154213253720451015410 0ustar travistravisLIBRARY segyio EXPORTS segy_open segy_mmap segy_flush segy_close segy_binheader_size segy_binheader segy_write_binheader segy_samples segy_sample_interval segy_format segy_get_field segy_get_bfield segy_set_field segy_set_bfield segy_field_forall segy_trace_bsize segy_trace0 segy_traces segy_sample_indices segy_read_textheader segy_textheader_size segy_read_ext_textheader segy_write_textheader segy_traceheader segy_write_traceheader segy_sorting segy_offsets segy_offset_indices segy_readtrace segy_readsubtr segy_writetrace segy_writesubtr segy_to_native segy_from_native segy_read_line segy_write_line segy_count_lines segy_lines_count segy_inline_length segy_crossline_length segy_inline_indices segy_crossline_indices segy_line_trace0 segy_inline_stride segy_crossline_stride segy_rotation_cw segy_seek segy_ftell ebcdic2ascii ascii2ebcdic ieee2ibm ibm2ieee segyio-1.5.2/lib/src/segyio/0000775000372000037200000000000013253720451015256 5ustar travistravissegyio-1.5.2/lib/src/segyio/util.h0000664000372000037200000000125013253720451016402 0ustar travistravis#ifndef SEGYIO_UTILS_H #define SEGYIO_UTILS_H #include /* * Functions that are internal implementations detail, but exposed to the * testing utilities. These functions won't show up in the installed headers. */ #ifdef __cplusplus extern "C" { #endif // __cplusplus struct segy_file_handle; void ebcdic2ascii( const char* ebcdic, char* ascii ); void ascii2ebcdic( const char* ascii, char* ebcdic ); void ibm2ieee(void* to, const void* from); void ieee2ibm(void* to, const void* from); int segy_seek( struct segy_file_handle*, int, long, int ); long long segy_ftell( struct segy_file_handle* ); #ifdef __cplusplus } #endif // __cplusplus #endif //SEGYIO_UTILS_H segyio-1.5.2/lib/src/segy.c0000664000372000037200000015266713253720451015113 0ustar travistravis#define _POSIX_SOURCE /* fileno */ /* 64-bit off_t in ftello */ #define _POSIX_C_SOURCE 200808L #define _FILE_OFFSET_BITS 64 #ifdef HAVE_MMAP #define _POSIX_SOURCE #include #endif //HAVE_MMAP #ifdef HAVE_NETINET_IN_H #include #elif HAVE_ARPA_INET_H #include #elif HAVE_WINSOCK2_H #include #endif #ifdef HAVE_SYS_STAT_H #include #include #endif //HAVE_SYS_STAT_H #include #include #include #include #include #include #include #include #include static const unsigned char a2e[256] = { 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15, 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31, 64, 79, 127,123,91, 108,80, 125,77, 93, 92, 78, 107,96, 75, 97, 240,241,242,243,244,245,246,247,248,249,122,94, 76, 126,110,111, 124,193,194,195,196,197,198,199,200,201,209,210,211,212,213,214, 215,216,217,226,227,228,229,230,231,232,233,74, 224,90, 95, 109, 121,129,130,131,132,133,134,135,136,137,145,146,147,148,149,150, 151,152,153,162,163,164,165,166,167,168,169,192,106,208,161,7, 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27, 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225, 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87, 88, 89, 98, 99, 100,101,102,103,104,105,112,113,114,115,116,117, 118,119,120,128,138,139,140,141,142,143,144,154,155,156,157,158, 159,160,170,171,172,173,174,175,176,177,178,179,180,181,182,183, 184,185,186,187,188,189,190,191,202,203,204,205,206,207,218,219, 220,221,222,223,234,235,236,237,238,239,250,251,252,253,254,255 }; static const unsigned char e2a[256] = { 0, 1, 2, 3, 156,9, 134,127,151,141,142, 11,12, 13, 14, 15, 16, 17, 18, 19, 157,133,8, 135,24, 25, 146,143,28, 29, 30, 31, 128,129,130,131,132,10, 23, 27, 136,137,138,139,140,5, 6, 7, 144,145,22, 147,148,149,150,4, 152,153,154,155,20, 21, 158,26, 32, 160,161,162,163,164,165,166,167,168,91, 46, 60, 40, 43, 33, 38, 169,170,171,172,173,174,175,176,177,93, 36, 42, 41, 59, 94, 45, 47, 178,179,180,181,182,183,184,185,124,44, 37, 95, 62, 63, 186,187,188,189,190,191,192,193,194,96, 58, 35, 64, 39, 61, 34, 195,97, 98, 99, 100,101,102,103,104,105,196,197,198,199,200,201, 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208, 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215, 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231, 123,65, 66, 67, 68, 69, 70, 71, 72, 73, 232,233,234,235,236,237, 125,74, 75, 76, 77, 78, 79, 80, 81, 82, 238,239,240,241,242,243, 92, 159,83, 84, 85, 86, 87, 88, 89, 90, 244,245,246,247,248,249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250,251,252,253,254,255 }; static int encode( char* dst, const char* src, const unsigned char* conv, size_t n ) { for( size_t i = 0; i < n; ++i ) dst[ i ] = (char)conv[ (unsigned char) src[ i ] ]; return SEGY_OK; } /* * DEPRECATED * ebcdic2ascii and ascii2ebcdic are deprecated in favour of the length-aware * encode. They will be removed in segyio2. These functions were never public * (in the sense they're not available in headers), but currently have external * linkage. */ void ebcdic2ascii( const char* ebcdic, char* ascii ) { size_t len = strlen( ebcdic ); encode( ascii, ebcdic, e2a, len ); ascii[ len ] = '\0'; } void ascii2ebcdic( const char* ascii, char* ebcdic ) { size_t len = strlen( ascii ); encode( ebcdic, ascii, a2e, len ); ebcdic[ len ] = '\0'; } #define IEEEMAX 0x7FFFFFFF #define IEMAXIB 0x611FFFFF #define IEMINIB 0x21200000 static inline void ibm_native( void* buf ) { static int it[8] = { 0x21800000, 0x21400000, 0x21000000, 0x21000000, 0x20c00000, 0x20c00000, 0x20c00000, 0x20c00000 }; static int mt[8] = { 8, 4, 2, 2, 1, 1, 1, 1 }; unsigned int manthi, iexp, inabs; int ix; uint32_t u; memcpy( &u, buf, sizeof( u ) ); manthi = u & 0x00ffffff; ix = manthi >> 21; iexp = ( ( u & 0x7f000000 ) - it[ix] ) << 1; manthi = manthi * mt[ix] + iexp; inabs = u & 0x7fffffff; if ( inabs > IEMAXIB ) manthi = IEEEMAX; manthi = manthi | ( u & 0x80000000 ); u = ( inabs < IEMINIB ) ? 0 : manthi; memcpy( buf, &u, sizeof( u ) ); } static inline void native_ibm( void* buf ) { static int it[4] = { 0x21200000, 0x21400000, 0x21800000, 0x22100000 }; static int mt[4] = { 2, 4, 8, 1 }; unsigned int manthi, iexp, ix; uint32_t u; memcpy( &u, buf, sizeof( u ) ); ix = ( u & 0x01800000 ) >> 23; iexp = ( ( u & 0x7e000000 ) >> 1 ) + it[ix]; manthi = ( mt[ix] * ( u & 0x007fffff) ) >> 3; manthi = ( manthi + iexp ) | ( u & 0x80000000 ); u = ( u & 0x7fffffff ) ? manthi : 0; memcpy( buf, &u, sizeof( u ) ); } void ibm2ieee( void* to, const void* from ) { uint32_t u; memcpy( &u, from, sizeof( u ) ); u = ntohl( u ); ibm_native( &u ); memcpy( to, &u, sizeof( u ) ); } void ieee2ibm( void* to, const void* from ) { uint32_t u; memcpy( &u, from, sizeof( u ) ); native_ibm( &u ); u = htonl( u ); memcpy( to, &u, sizeof( u ) ); } /* Lookup table for field sizes. All values not explicitly set are 0 */ static int field_size[] = { [SEGY_TR_CDP_X ] = 4, [SEGY_TR_CDP_Y ] = 4, [SEGY_TR_CROSSLINE ] = 4, [SEGY_TR_ENERGY_SOURCE_POINT ] = 4, [SEGY_TR_ENSEMBLE ] = 4, [SEGY_TR_FIELD_RECORD ] = 4, [SEGY_TR_GROUP_WATER_DEPTH ] = 4, [SEGY_TR_GROUP_X ] = 4, [SEGY_TR_GROUP_Y ] = 4, [SEGY_TR_INLINE ] = 4, [SEGY_TR_NUMBER_ORIG_FIELD ] = 4, [SEGY_TR_NUM_IN_ENSEMBLE ] = 4, [SEGY_TR_OFFSET ] = 4, [SEGY_TR_RECV_DATUM_ELEV ] = 4, [SEGY_TR_RECV_GROUP_ELEV ] = 4, [SEGY_TR_SEQ_FILE ] = 4, [SEGY_TR_SEQ_LINE ] = 4, [SEGY_TR_SHOT_POINT ] = 4, [SEGY_TR_SOURCE_DATUM_ELEV ] = 4, [SEGY_TR_SOURCE_DEPTH ] = 4, [SEGY_TR_SOURCE_ENERGY_DIR_MANT ] = 4, [SEGY_TR_SOURCE_MEASURE_MANT ] = 4, [SEGY_TR_SOURCE_SURF_ELEV ] = 4, [SEGY_TR_SOURCE_X ] = 4, [SEGY_TR_SOURCE_Y ] = 4, [SEGY_TR_TRANSDUCTION_MANT ] = 4, [SEGY_TR_UNASSIGNED1 ] = 4, [SEGY_TR_UNASSIGNED2 ] = 4, [SEGY_TR_ALIAS_FILT_FREQ ] = 2, [SEGY_TR_ALIAS_FILT_SLOPE ] = 2, [SEGY_TR_COORD_UNITS ] = 2, [SEGY_TR_CORRELATED ] = 2, [SEGY_TR_DATA_USE ] = 2, [SEGY_TR_DAY_OF_YEAR ] = 2, [SEGY_TR_DELAY_REC_TIME ] = 2, [SEGY_TR_DEVICE_ID ] = 2, [SEGY_TR_ELEV_SCALAR ] = 2, [SEGY_TR_GAIN_TYPE ] = 2, [SEGY_TR_GAP_SIZE ] = 2, [SEGY_TR_GEOPHONE_GROUP_FIRST ] = 2, [SEGY_TR_GEOPHONE_GROUP_LAST ] = 2, [SEGY_TR_GEOPHONE_GROUP_ROLL1 ] = 2, [SEGY_TR_GROUP_STATIC_CORR ] = 2, [SEGY_TR_GROUP_UPHOLE_TIME ] = 2, [SEGY_TR_HIGH_CUT_FREQ ] = 2, [SEGY_TR_HIGH_CUT_SLOPE ] = 2, [SEGY_TR_HOUR_OF_DAY ] = 2, [SEGY_TR_INSTR_GAIN_CONST ] = 2, [SEGY_TR_INSTR_INIT_GAIN ] = 2, [SEGY_TR_LAG_A ] = 2, [SEGY_TR_LAG_B ] = 2, [SEGY_TR_LOW_CUT_FREQ ] = 2, [SEGY_TR_LOW_CUT_SLOPE ] = 2, [SEGY_TR_MEASURE_UNIT ] = 2, [SEGY_TR_MIN_OF_HOUR ] = 2, [SEGY_TR_MUTE_TIME_END ] = 2, [SEGY_TR_MUTE_TIME_START ] = 2, [SEGY_TR_NOTCH_FILT_FREQ ] = 2, [SEGY_TR_NOTCH_FILT_SLOPE ] = 2, [SEGY_TR_OVER_TRAVEL ] = 2, [SEGY_TR_SAMPLE_COUNT ] = 2, [SEGY_TR_SAMPLE_INTER ] = 2, [SEGY_TR_SCALAR_TRACE_HEADER ] = 2, [SEGY_TR_SEC_OF_MIN ] = 2, [SEGY_TR_SHOT_POINT_SCALAR ] = 2, [SEGY_TR_SOURCE_ENERGY_DIR_EXP ] = 2, [SEGY_TR_SOURCE_GROUP_SCALAR ] = 2, [SEGY_TR_SOURCE_MEASURE_EXP ] = 2, [SEGY_TR_SOURCE_MEASURE_UNIT ] = 2, [SEGY_TR_SOURCE_STATIC_CORR ] = 2, [SEGY_TR_SOURCE_TYPE ] = 2, [SEGY_TR_SOURCE_UPHOLE_TIME ] = 2, [SEGY_TR_SOURCE_WATER_DEPTH ] = 2, [SEGY_TR_STACKED_TRACES ] = 2, [SEGY_TR_SUBWEATHERING_VELO ] = 2, [SEGY_TR_SUMMED_TRACES ] = 2, [SEGY_TR_SWEEP_FREQ_END ] = 2, [SEGY_TR_SWEEP_FREQ_START ] = 2, [SEGY_TR_SWEEP_LENGTH ] = 2, [SEGY_TR_SWEEP_TAPERLEN_END ] = 2, [SEGY_TR_SWEEP_TAPERLEN_START ] = 2, [SEGY_TR_SWEEP_TYPE ] = 2, [SEGY_TR_TAPER_TYPE ] = 2, [SEGY_TR_TIME_BASE_CODE ] = 2, [SEGY_TR_TOT_STATIC_APPLIED ] = 2, [SEGY_TR_TRACE_ID ] = 2, [SEGY_TR_TRANSDUCTION_EXP ] = 2, [SEGY_TR_TRANSDUCTION_UNIT ] = 2, [SEGY_TR_WEATHERING_VELO ] = 2, [SEGY_TR_WEIGHTING_FAC ] = 2, [SEGY_TR_YEAR_DATA_REC ] = 2, }; #define HEADER_SIZE SEGY_TEXT_HEADER_SIZE /* * Supporting same byte offsets as in the segy specification, i.e. from the * start of the *text header*, not the binary header. */ static int bfield_size[] = { [- HEADER_SIZE + SEGY_BIN_JOB_ID ] = 4, [- HEADER_SIZE + SEGY_BIN_LINE_NUMBER ] = 4, [- HEADER_SIZE + SEGY_BIN_REEL_NUMBER ] = 4, [- HEADER_SIZE + SEGY_BIN_TRACES ] = 2, [- HEADER_SIZE + SEGY_BIN_AUX_TRACES ] = 2, [- HEADER_SIZE + SEGY_BIN_INTERVAL ] = 2, [- HEADER_SIZE + SEGY_BIN_INTERVAL_ORIG ] = 2, [- HEADER_SIZE + SEGY_BIN_SAMPLES ] = 2, [- HEADER_SIZE + SEGY_BIN_SAMPLES_ORIG ] = 2, [- HEADER_SIZE + SEGY_BIN_FORMAT ] = 2, [- HEADER_SIZE + SEGY_BIN_ENSEMBLE_FOLD ] = 2, [- HEADER_SIZE + SEGY_BIN_SORTING_CODE ] = 2, [- HEADER_SIZE + SEGY_BIN_VERTICAL_SUM ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_FREQ_START ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_FREQ_END ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_LENGTH ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_CHANNEL ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_TAPER_START ] = 2, [- HEADER_SIZE + SEGY_BIN_SWEEP_TAPER_END ] = 2, [- HEADER_SIZE + SEGY_BIN_TAPER ] = 2, [- HEADER_SIZE + SEGY_BIN_CORRELATED_TRACES ] = 2, [- HEADER_SIZE + SEGY_BIN_BIN_GAIN_RECOVERY ] = 2, [- HEADER_SIZE + SEGY_BIN_AMPLITUDE_RECOVERY ] = 2, [- HEADER_SIZE + SEGY_BIN_MEASUREMENT_SYSTEM ] = 2, [- HEADER_SIZE + SEGY_BIN_IMPULSE_POLARITY ] = 2, [- HEADER_SIZE + SEGY_BIN_VIBRATORY_POLARITY ] = 2, [- HEADER_SIZE + SEGY_BIN_SEGY_REVISION ] = 2, [- HEADER_SIZE + SEGY_BIN_TRACE_FLAG ] = 2, [- HEADER_SIZE + SEGY_BIN_EXT_HEADERS ] = 2, [- HEADER_SIZE + SEGY_BIN_UNASSIGNED1 ] = 0, [- HEADER_SIZE + SEGY_BIN_UNASSIGNED2 ] = 0, }; /* * Determine the file size in bytes. If this function succeeds, the file * pointer will be reset to wherever it was before this call. If this call * fails for some reason, the return value is 0 and the file pointer location * will be determined by the behaviour of fseek. * * sys/stat.h is POSIX, but is well enough supported by Windows. The long long * data type is required to support files >4G (as long only guarantees 32 bits). */ #ifdef HAVE_SYS_STAT_H static int file_size( FILE* fp, long long* size ) { /* * the file size will be unaccurate unless userland buffers are flushed if * the file is new or appended to */ if( fflush( fp ) != 0 ) return SEGY_FWRITE_ERROR; #ifdef HAVE_FSTATI64 // this means we're on windows where fstat is unreliable for filesizes >2G // because long is only 4 bytes struct _stati64 st; const int err = _fstati64( fileno( fp ), &st ); #else struct stat st; const int err = fstat( fileno( fp ), &st ); #endif if( err != 0 ) return SEGY_FSEEK_ERROR; *size = st.st_size; return SEGY_OK; } #endif //HAVE_SYS_STAT_H /* * addr is NULL if mmap is not found under compilation or if the file is * not requested mmap'd. If so, the fallback code path of FILE* is taken */ #define MODEBUF_SIZE 5 struct segy_file_handle { void* addr; void* cur; FILE* fp; size_t fsize; char mode[ MODEBUF_SIZE ]; int writable; }; segy_file* segy_open( const char* path, const char* mode ) { if( !path || !mode ) return NULL; // append a 'b' if it is not passed by the user; not a problem on unix, but // windows and other platforms fail without it char binary_mode[ MODEBUF_SIZE ] = { 0 }; strncpy( binary_mode, mode, 3 ); size_t mode_len = strlen( binary_mode ); if( binary_mode[ mode_len - 1 ] != 'b' ) binary_mode[ mode_len ] = 'b'; // Account for invalid mode. On unix this is fine, but windows crashes the // process if mode is invalid if( !strstr( "rb" "wb" "ab" "r+b" "w+b" "a+b", binary_mode ) ) return NULL; FILE* fp = fopen( path, binary_mode ); if( !fp ) return NULL; segy_file* file = calloc( 1, sizeof( segy_file ) ); if( !file ) { fclose( fp ); return NULL; } file->fp = fp; strcpy( file->mode, binary_mode ); bool rw = strstr( file->mode, "+" ) || strstr( file->mode, "w" ); if( rw ) file->writable = 1; return file; } int segy_mmap( segy_file* fp ) { #ifndef HAVE_MMAP return SEGY_MMAP_INVALID; #else /* don't re-map; i.e. multiple consecutive calls should be no-ops */ if( fp->addr ) return SEGY_OK; long long fsize; int err = file_size( fp->fp, &fsize ); if( err != 0 ) return SEGY_FSEEK_ERROR; const int prot = fp->writable ? PROT_READ | PROT_WRITE : PROT_READ; int fd = fileno( fp->fp ); void* addr = mmap( NULL, fsize, prot, MAP_SHARED, fd, 0 ); // cppcheck-suppress memleak if( addr == MAP_FAILED ) return SEGY_MMAP_ERROR; fp->addr = fp->cur = addr; fp->fsize = fsize; fclose(fp->fp); return SEGY_OK; #endif //HAVE_MMAP } int segy_flush( segy_file* fp, bool async ) { // flush is a no-op for read-only files if( !fp->writable ) return SEGY_OK; #ifdef HAVE_MMAP if( fp->addr ) { int flag = async ? MS_ASYNC : MS_SYNC; int syncerr = msync( fp->addr, fp->fsize, flag ); if( syncerr != 0 ) return syncerr; return SEGY_OK; } #endif //HAVE_MMAP int flusherr = fflush( fp->fp ); if( flusherr != 0 ) return SEGY_FWRITE_ERROR; return SEGY_OK; } long long segy_ftell( segy_file* fp ) { #ifdef HAVE_FTELLO off_t pos = ftello( fp->fp ); assert( pos != -1 ); return pos; #elif HAVE_FTELLI64 // assuming we're on windows. This function is a little rough, but only // meant for testing - it's not a part of the public interface. return _ftelli64( fp->fp ); #else assert( false ); #endif } int segy_close( segy_file* fp ) { int err = segy_flush( fp, false ); #ifdef HAVE_MMAP if( !fp->addr ) goto no_mmap; err = munmap( fp->addr, fp->fsize ); if( err != 0 ) err = SEGY_MMAP_ERROR; free( fp ); return err; no_mmap: #endif //HAVE_MMAP fclose( fp->fp ); free( fp ); return err; } static int get_field( const char* header, const int* table, int field, int32_t* f ) { const int bsize = table[ field ]; uint32_t buf32 = 0; uint16_t buf16 = 0; switch( bsize ) { case 4: memcpy( &buf32, header + (field - 1), 4 ); *f = (int32_t)ntohl( buf32 ); return SEGY_OK; case 2: memcpy( &buf16, header + (field - 1), 2 ); *f = (int16_t)ntohs( buf16 ); return SEGY_OK; case 0: default: return SEGY_INVALID_FIELD; } } int segy_get_field( const char* traceheader, int field, int* f ) { if( field < 0 || field >= SEGY_TRACE_HEADER_SIZE ) return SEGY_INVALID_FIELD; return get_field( traceheader, field_size, field, f ); } int segy_get_bfield( const char* binheader, int field, int32_t* f ) { field -= SEGY_TEXT_HEADER_SIZE; if( field < 0 || field >= SEGY_BINARY_HEADER_SIZE ) return SEGY_INVALID_FIELD; return get_field( binheader, bfield_size, field, f ); } static int set_field( char* header, const int* table, int field, int32_t val ) { const int bsize = table[ field ]; uint32_t buf32; uint16_t buf16; switch( bsize ) { case 4: buf32 = htonl( (uint32_t)val ); memcpy( header + (field - 1), &buf32, sizeof( buf32 ) ); return SEGY_OK; case 2: buf16 = htons( (uint16_t)val ); memcpy( header + (field - 1), &buf16, sizeof( buf16 ) ); return SEGY_OK; case 0: default: return SEGY_INVALID_FIELD; } } int segy_set_field( char* traceheader, int field, int val ) { if( field < 0 || field >= SEGY_TRACE_HEADER_SIZE ) return SEGY_INVALID_FIELD; return set_field( traceheader, field_size, field, val ); } int segy_set_bfield( char* binheader, int field, int val ) { field -= SEGY_TEXT_HEADER_SIZE; if( field < 0 || field >= SEGY_BINARY_HEADER_SIZE ) return SEGY_INVALID_FIELD; return set_field( binheader, bfield_size, field, val ); } static int slicelength( int start, int stop, int step ) { if( step == 0 ) return 0; if( ( step < 0 && stop >= start ) || ( step > 0 && start >= stop ) ) return 0; if( step < 0 ) return (stop - start + 1) / step + 1; return (stop - start - 1) / step + 1; } int segy_field_forall( segy_file* fp, int field, int start, int stop, int step, int* buf, long trace0, int trace_bsize ) { int err; // do a dummy-read of a zero-init'd buffer to check args int32_t f; char header[ SEGY_TRACE_HEADER_SIZE ] = { 0 }; err = segy_get_field( header, field, &f ); if( err != SEGY_OK ) return SEGY_INVALID_ARGS; int slicelen = slicelength( start, stop, step ); // check *once* that we don't look past the end-of-file // checking seek error inside the loop is a performance killer err = segy_seek( fp, start, trace0, trace_bsize ); if( err != SEGY_OK ) return err; const int end = start + step * (slicelen - 1); err = segy_seek( fp, end, trace0, trace_bsize ); if( err != SEGY_OK ) return err; #ifdef HAVE_MMAP if( fp->addr ) { for( int i = start; slicelen > 0; i += step, ++buf, --slicelen ) { segy_seek( fp, i, trace0, trace_bsize ); get_field( fp->cur, field_size, field, &f ); *buf = f; } return SEGY_OK; } #endif //HAVE_MMAP /* * non-mmap path. Doing multiple freads is slow, so instead the *actual* * offset is computed, not just the start of the header, and that's copied * into the correct offset in our local buffer. Note that byte offsets are * exposed 1-indexed (to stay consistent with the specification), but the * buffers are 0-indexed. * * Always read 4 bytes to be sure, there's no significant cost difference. */ const int zfield = field - 1; for( int i = start; slicelen > 0; i += step, ++buf, --slicelen ) { err = segy_seek( fp, i, trace0 + zfield, trace_bsize ); if( err != 0 ) return SEGY_FSEEK_ERROR; size_t readc = fread( header + zfield, sizeof( uint32_t ), 1, fp->fp ); if( readc != 1 ) return SEGY_FREAD_ERROR; segy_get_field( header, field, &f ); *buf = f; } return SEGY_OK; } /* * memread/memwrite are small utilities to give reading/writing to * memory-mapped files fread/fwrite like behaviour and fail if going outside * the file. Returns SEGY_FREAD/WRITE_ERROR, so that functions that are * read-and-return can just return this function's result */ static int memread( void* dest, const segy_file* fp, const void* src, size_t n ) { const void* begin = fp->addr; const void* end = (const char*)fp->addr + fp->fsize; const void* srcend = (const char*)src + n; if( src < begin || src > end || srcend > end ) return SEGY_FREAD_ERROR; memcpy( dest, src, n ); return SEGY_OK; } static int memwrite( segy_file* fp, void* dest, const void* src, size_t n ) { const void* begin = fp->addr; const void* end = (const char*)fp->addr + fp->fsize; const void* destend = (const char*)dest + n; if( dest < begin || dest > end || destend > end ) return SEGY_FWRITE_ERROR; memcpy( dest, src, n ); return SEGY_OK; } int segy_binheader( segy_file* fp, char* buf ) { if(fp == NULL) { return SEGY_INVALID_ARGS; } #ifdef HAVE_MMAP if( fp->addr ) { return memread( buf, fp, (char*)fp->addr + SEGY_TEXT_HEADER_SIZE, SEGY_BINARY_HEADER_SIZE ); } #endif //HAVE_MMAP const int err = fseek( fp->fp, SEGY_TEXT_HEADER_SIZE, SEEK_SET ); if( err != 0 ) return SEGY_FSEEK_ERROR; const size_t read_count = fread( buf, 1, SEGY_BINARY_HEADER_SIZE, fp->fp ); if( read_count != SEGY_BINARY_HEADER_SIZE ) return SEGY_FREAD_ERROR; return SEGY_OK; } int segy_write_binheader( segy_file* fp, const char* buf ) { if( !fp->writable ) return SEGY_READONLY; #ifdef HAVE_MMAP if( fp->addr ) { return memwrite( fp, (char*)fp->addr + SEGY_TEXT_HEADER_SIZE, buf, SEGY_BINARY_HEADER_SIZE ); } #endif //HAVE_MMAP const int err = fseek( fp->fp, SEGY_TEXT_HEADER_SIZE, SEEK_SET ); if( err != 0 ) return SEGY_FSEEK_ERROR; const size_t writec = fwrite( buf, 1, SEGY_BINARY_HEADER_SIZE, fp->fp ); if( writec != SEGY_BINARY_HEADER_SIZE ) return SEGY_FWRITE_ERROR; return SEGY_OK; } int segy_format( const char* binheader ) { int32_t format = 0; segy_get_bfield( binheader, SEGY_BIN_FORMAT, &format ); return format; } int segy_samples( const char* binheader ) { int32_t samples = 0; segy_get_bfield( binheader, SEGY_BIN_SAMPLES, &samples ); return samples; } int segy_trace_bsize( int samples ) { assert( samples >= 0 ); /* Hard four-byte float assumption */ return samples * 4; } long segy_trace0( const char* binheader ) { int extra_headers = 0; segy_get_bfield( binheader, SEGY_BIN_EXT_HEADERS, &extra_headers ); return SEGY_TEXT_HEADER_SIZE + SEGY_BINARY_HEADER_SIZE + SEGY_TEXT_HEADER_SIZE * extra_headers; } int segy_seek( segy_file* fp, int trace, long trace0, int trace_bsize ) { trace_bsize += SEGY_TRACE_HEADER_SIZE; long long pos = (long long)trace0 + (trace * (long long)trace_bsize); #ifdef HAVE_MMAP if( fp->addr ) { /* * mmap fseek doesn't fail (it's just a pointer readjustment) and won't * set errno, in order to keep its behaviour consistent with fseek, * which can easily reposition itself past the end-of-file */ fp->cur = (char*)fp->addr + pos; return SEGY_OK; } #endif //HAVE_MMAP int err; #if LONG_MAX == LLONG_MAX assert( pos <= LONG_MAX ); err = fseek( fp->fp, (long)pos, SEEK_SET ); #else /* * If long is 32bit on our platform (hello, windows), we do skips according * to LONG_MAX and seek relative to our cursor rather than absolute on file * begin. */ err = SEGY_OK; rewind( fp->fp ); while( pos >= LONG_MAX && err == SEGY_OK ) { err = fseek( fp->fp, LONG_MAX, SEEK_CUR ); pos -= LONG_MAX; } if( err != 0 ) return SEGY_FSEEK_ERROR; assert( pos <= LONG_MAX ); err = fseek( fp->fp, (long)pos, SEEK_CUR ); #endif if( err != 0 ) return SEGY_FSEEK_ERROR; return SEGY_OK; } int segy_traceheader( segy_file* fp, int traceno, char* buf, long trace0, int trace_bsize ) { const int err = segy_seek( fp, traceno, trace0, trace_bsize ); if( err != 0 ) return err; if( fp->addr ) return memread( buf, fp, fp->cur, SEGY_TRACE_HEADER_SIZE ); const size_t readc = fread( buf, 1, SEGY_TRACE_HEADER_SIZE, fp->fp ); if( readc != SEGY_TRACE_HEADER_SIZE ) return SEGY_FREAD_ERROR; return SEGY_OK; } int segy_write_traceheader( segy_file* fp, int traceno, const char* buf, long trace0, int trace_bsize ) { if( !fp->writable ) return SEGY_READONLY; const int err = segy_seek( fp, traceno, trace0, trace_bsize ); if( err != 0 ) return err; if( fp->addr ) return memwrite( fp, fp->cur, buf, SEGY_TRACE_HEADER_SIZE ); const size_t writec = fwrite( buf, 1, SEGY_TRACE_HEADER_SIZE, fp->fp ); if( writec != SEGY_TRACE_HEADER_SIZE ) return SEGY_FWRITE_ERROR; return SEGY_OK; } /* * Return the number of traces in the file. The file pointer won't change after * this call unless fseek itself fails. * * This function assumes that *all traces* are of the same size. */ int segy_traces( segy_file* fp, int* traces, long trace0, int trace_bsize ) { if( trace0 < 0 ) return SEGY_INVALID_ARGS; long long size; if( fp->addr ) size = fp->fsize; else{ int err = file_size( fp->fp, &size ); if( err != 0 ) return err; } if( trace0 > size ) return SEGY_INVALID_ARGS; size -= trace0; trace_bsize += SEGY_TRACE_HEADER_SIZE; if( size % trace_bsize != 0 ) return SEGY_TRACE_SIZE_MISMATCH; assert( size / trace_bsize <= (long long)INT_MAX ); *traces = size / trace_bsize; return SEGY_OK; } int segy_sample_interval( segy_file* fp, float fallback, float* dt ) { char bin_header[ SEGY_BINARY_HEADER_SIZE ]; char trace_header[ SEGY_TRACE_HEADER_SIZE ]; int err = segy_binheader( fp, bin_header ); if (err != 0) { return err; } const long trace0 = segy_trace0( bin_header ); int samples = segy_samples( bin_header ); const int trace_bsize = segy_trace_bsize( samples ); err = segy_traceheader(fp, 0, trace_header, trace0, trace_bsize); if (err != 0) { return err; } int bindt = 0; int trdt = 0; segy_get_bfield( bin_header, SEGY_BIN_INTERVAL, &bindt ); segy_get_field( trace_header, SEGY_TR_SAMPLE_INTER, &trdt ); float binary_header_dt = bindt; float trace_header_dt = trdt; /* * 3 cases: * * When the trace header and binary header disagree on a (non-zero) * sample interval; choose neither and opt for the fallback. * * When both sample intervals are zero: opt for the fallback. * * Otherwise, choose the interval from the non-zero header. */ *dt = fallback; if( binary_header_dt == 0 && trace_header_dt != 0 ) *dt = trace_header_dt; if( trace_header_dt == 0 && binary_header_dt != 0 ) *dt = binary_header_dt; if( trace_header_dt == binary_header_dt && trace_header_dt != 0 ) *dt = trace_header_dt; return SEGY_OK; } int segy_sample_indices( segy_file* fp, float t0, float dt, int count, float* buf ) { int err = segy_sample_interval(fp, dt, &dt); if (err != 0) { return err; } for( int i = 0; i < count; i++ ) { buf[i] = t0 + i * dt; } return SEGY_OK; } /* * Determine how a file is sorted. Expects the following two fields from the * trace header to guide sorting: the inline number `il` and the crossline * number `xl`. * * Inspects trace headers 0 and 1 and compares these two fields in the * respective trace headers. If the first two traces are components of the same * inline, header[0].ilnum should be equal to header[1].ilnum, similarly for * crosslines. If neither match, the sorting is considered unknown. */ int segy_sorting( segy_file* fp, int il, int xl, int tr_offset, int* sorting, long trace0, int trace_bsize ) { int err; char traceheader[ SEGY_TRACE_HEADER_SIZE ]; err = segy_traceheader( fp, 0, traceheader, trace0, trace_bsize ); if( err != SEGY_OK ) return err; if( il < 0 || il >= SEGY_TRACE_HEADER_SIZE ) return SEGY_INVALID_FIELD; if( xl < 0 || xl >= SEGY_TRACE_HEADER_SIZE ) return SEGY_INVALID_FIELD; /* make sure field is valid, so we don't have to check errors later */ if( field_size[ il ] == 0 || field_size[ xl ] == 0 ) return SEGY_INVALID_FIELD; int il0 = 0, xl0 = 0, il1 = 0, xl1 = 0, off0 = 0, off1 = 0; segy_get_field( traceheader, il, &il0 ); segy_get_field( traceheader, xl, &xl0 ); segy_get_field( traceheader, tr_offset, &off0 ); int traces; err = segy_traces( fp, &traces, trace0, trace_bsize ); if( err != 0 ) return err; int traceno = 1; do { err = segy_traceheader( fp, traceno, traceheader, trace0, trace_bsize ); if( err != SEGY_OK ) return err; segy_get_field( traceheader, il, &il1 ); segy_get_field( traceheader, xl, &xl1 ); segy_get_field( traceheader, tr_offset, &off1 ); ++traceno; } while( off0 != off1 && traceno < traces ); /* * sometimes files come with Mx1, 1xN or even 1x1 geometries. When this is * the case we look at the last trace and compare it to the first. If these * numbers match we define the sorting direction as the non-1 dimension */ err = segy_traceheader( fp, traces - 1, traceheader, trace0, trace_bsize ); if( err != SEGY_OK ) return err; int il_last = 0, xl_last = 0; segy_get_field( traceheader, il, &il_last ); segy_get_field( traceheader, xl, &xl_last ); if ( il0 == il_last ) *sorting = SEGY_CROSSLINE_SORTING; else if( xl0 == xl_last ) *sorting = SEGY_INLINE_SORTING; else if( il0 == il1 ) *sorting = SEGY_INLINE_SORTING; else if( xl0 == xl1 ) *sorting = SEGY_CROSSLINE_SORTING; else return SEGY_INVALID_SORTING; return SEGY_OK; } /* * Find the number of offsets. This is determined by inspecting the trace * headers [0,n) where n is the first trace where either the inline number or * the crossline number changes (which changes first depends on sorting, but is * irrelevant for this function). */ int segy_offsets( segy_file* fp, int il, int xl, int traces, int* out, long trace0, int trace_bsize ) { int err; int il0 = 0, il1 = 0, xl0 = 0, xl1 = 0; char header[ SEGY_TRACE_HEADER_SIZE ]; int offsets = 0; if( traces == 1 ) { *out = 1; return SEGY_OK; } /* * check that field value is sane, so that we don't have to check * segy_get_field's error */ if( field_size[ il ] == 0 || field_size[ xl ] == 0 ) return SEGY_INVALID_FIELD; err = segy_traceheader( fp, 0, header, trace0, trace_bsize ); if( err != 0 ) return SEGY_FREAD_ERROR; segy_get_field( header, il, &il0 ); segy_get_field( header, xl, &xl0 ); do { ++offsets; if( offsets == traces ) break; err = segy_traceheader( fp, offsets, header, trace0, trace_bsize ); if( err != 0 ) return err; segy_get_field( header, il, &il1 ); segy_get_field( header, xl, &xl1 ); } while( il0 == il1 && xl0 == xl1 ); *out = offsets; return SEGY_OK; } int segy_offset_indices( segy_file* fp, int offset_field, int offsets, int* out, long trace0, int trace_bsize ) { int32_t x = 0; char header[ SEGY_TRACE_HEADER_SIZE ]; if( field_size[ offset_field ] == 0 ) return SEGY_INVALID_FIELD; for( int i = 0; i < offsets; ++i ) { const int err = segy_traceheader( fp, i, header, trace0, trace_bsize ); if( err != SEGY_OK ) return err; segy_get_field( header, offset_field, &x ); *out++ = x; } return SEGY_OK; } static int segy_line_indices( segy_file* fp, int field, int traceno, int stride, int num_indices, int* buf, long trace0, int trace_bsize ) { return segy_field_forall( fp, field, traceno, /* start */ traceno + (num_indices * stride), /* stop */ stride, /* step */ buf, trace0, trace_bsize ); } static int count_lines( segy_file* fp, int field, int offsets, int traces, int* out, long trace0, int trace_bsize ) { int err; char header[ SEGY_TRACE_HEADER_SIZE ]; err = segy_traceheader( fp, 0, header, trace0, trace_bsize ); if( err != 0 ) return err; int first_lineno, first_offset, ln = 0, off = 0; err = segy_get_field( header, field, &first_lineno ); if( err != 0 ) return err; err = segy_get_field( header, 37, &first_offset ); if( err != 0 ) return err; int lines = 1; int curr = offsets; while( true ) { if( curr >= traces ) return SEGY_NOTFOUND; err = segy_traceheader( fp, curr, header, trace0, trace_bsize ); if( err != 0 ) return err; segy_get_field( header, field, &ln ); segy_get_field( header, 37, &off ); if( first_offset == off && ln == first_lineno ) break; curr += offsets; ++lines; } *out = lines; return SEGY_OK; } int segy_count_lines( segy_file* fp, int field, int offsets, int* l1out, int* l2out, long trace0, int trace_bsize ) { int traces; int err = segy_traces( fp, &traces, trace0, trace_bsize ); if( err != 0 ) return err; /* * handle the case where there's only one trace (per offset) in the file, * and interpret is as a 1 line in each direction, with 1 trace (per * offset). */ if( traces == offsets ) { *l1out = *l2out = 1; return SEGY_OK; } int l2count; err = count_lines( fp, field, offsets, traces, &l2count, trace0, trace_bsize ); if( err != 0 ) return err; const int line_length = l2count * offsets; const int l1count = traces / line_length; *l1out = l1count; *l2out = l2count; return SEGY_OK; } int segy_lines_count( segy_file* fp, int il, int xl, int sorting, int offsets, int* il_count, int* xl_count, long trace0, int trace_bsize ) { if( sorting == SEGY_UNKNOWN_SORTING ) return SEGY_INVALID_SORTING; int field; int l1out, l2out; if( sorting == SEGY_INLINE_SORTING ) field = xl; else field = il; int err = segy_count_lines( fp, field, offsets, &l1out, &l2out, trace0, trace_bsize ); if( err != SEGY_OK ) return err; if( sorting == SEGY_INLINE_SORTING ) { *il_count = l1out; *xl_count = l2out; } else { *il_count = l2out; *xl_count = l1out; } return SEGY_OK; } /* * segy_*line_length is rather pointless as a computation, but serve a purpose * as an abstraction as the detail on how exactly a length is defined is usually uninteresting */ int segy_inline_length( int crossline_count ) { return crossline_count; } int segy_crossline_length( int inline_count ) { return inline_count; } int segy_inline_indices( segy_file* fp, int il, int sorting, int inline_count, int crossline_count, int offsets, int* buf, long trace0, int trace_bsize) { if( sorting == SEGY_INLINE_SORTING ) { int stride = crossline_count * offsets; return segy_line_indices( fp, il, 0, stride, inline_count, buf, trace0, trace_bsize ); } if( sorting == SEGY_CROSSLINE_SORTING ) { return segy_line_indices( fp, il, 0, offsets, inline_count, buf, trace0, trace_bsize ); } return SEGY_INVALID_SORTING; } int segy_crossline_indices( segy_file* fp, int xl, int sorting, int inline_count, int crossline_count, int offsets, int* buf, long trace0, int trace_bsize ) { if( sorting == SEGY_INLINE_SORTING ) { return segy_line_indices( fp, xl, 0, offsets, crossline_count, buf, trace0, trace_bsize ); } if( sorting == SEGY_CROSSLINE_SORTING ) { int stride = inline_count * offsets; return segy_line_indices( fp, xl, 0, stride, crossline_count, buf, trace0, trace_bsize ); } return SEGY_INVALID_SORTING; } static inline int subtr_seek( segy_file* fp, int traceno, int start, int stop, long trace0, int trace_bsize ) { /* * Optimistically assume that indices are correct by the time they're given * to subtr_seek. */ int min = start < stop ? start : stop + 1; assert( sizeof( float ) == 4 ); assert( start >= 0 ); assert( stop >= -1 ); assert( abs(stop - start) * (int)sizeof( float ) <= trace_bsize ); // skip the trace header and skip everything before min trace0 += (min * (int)sizeof( float )) + SEGY_TRACE_HEADER_SIZE; return segy_seek( fp, traceno, trace0, trace_bsize ); } static int reverse( float* arr, int elems ) { const int last = elems - 1; for( int i = 0; i < elems / 2; ++i ) { const float tmp = arr[ i ]; arr[ i ] = arr[ last - i ]; arr[ last - i ] = tmp; } return SEGY_OK; } int segy_readtrace( segy_file* fp, int traceno, float* buf, long trace0, int trace_bsize ) { const int stop = trace_bsize / sizeof( float ); return segy_readsubtr( fp, traceno, 0, stop, 1, buf, NULL, trace0, trace_bsize ); } int segy_readsubtr( segy_file* fp, int traceno, int start, int stop, int step, float* buf, float* rangebuf, long trace0, int trace_bsize ) { int err = subtr_seek( fp, traceno, start, stop, trace0, trace_bsize ); if( err != SEGY_OK ) return err; const size_t elems = abs( stop - start ); // most common case: step == abs(1), reading contiguously if( step == 1 || step == -1 ) { if( fp->addr ) { err = memread( buf, fp, fp->cur, sizeof( float ) * elems ); if( err != SEGY_OK ) return err; } else { const size_t readc = fread( buf, sizeof( float ), elems, fp->fp ); if( readc != elems ) return SEGY_FREAD_ERROR; } if( step == -1 ) reverse( buf, elems ); return SEGY_OK; } // step != 1, i.e. do strided reads int defstart = start < stop ? 0 : elems - 1; int slicelen = slicelength( start, stop, step ); if( fp->addr ) { float* cur = (float*)fp->cur + defstart; for( ; slicelen > 0; cur += step, ++buf, --slicelen ) *buf = *cur; return SEGY_OK; } /* * fread fallback: read the full chunk [start, stop) to avoid multiple * fread calls (which are VERY expensive, measured to about 10x the cost of * a single read when reading every other trace). If rangebuf is NULL, the * caller has not supplied a buffer for us to use (likely if it's a * one-off, and we heap-alloc a buffer. This way the function is safer to * use, but with a significant performance penalty when no buffer is * supplied. */ float* tracebuf = rangebuf ? rangebuf : malloc( elems * sizeof( float ) ); const size_t readc = fread( tracebuf, sizeof( float ), elems, fp->fp ); if( readc != elems ) { if( !rangebuf ) free( tracebuf ); return SEGY_FREAD_ERROR; } float* cur = tracebuf + defstart; for( ; slicelen > 0; cur += step, --slicelen, ++buf ) *buf = *cur; if( !rangebuf ) free( tracebuf ); return SEGY_OK; } int segy_writetrace( segy_file* fp, int traceno, const float* buf, long trace0, int trace_bsize ) { const int stop = trace_bsize / sizeof( float ); return segy_writesubtr( fp, traceno, 0, stop, 1, buf, NULL, trace0, trace_bsize ); } int segy_writesubtr( segy_file* fp, int traceno, int start, int stop, int step, const float* buf, float* rangebuf, long trace0, int trace_bsize ) { if( !fp->writable ) return SEGY_READONLY; int err = subtr_seek( fp, traceno, start, stop, trace0, trace_bsize ); if( err != SEGY_OK ) return err; const size_t elems = abs( stop - start ); if( step == 1 ) { /* * most common case: step == 1, writing contiguously * -1 is not covered here as it would require reversing the input buffer * (which is const), which in turn may require a memory allocation. It will * be handled by the stride-aware code path */ if( fp->addr ) { err = memread( fp, fp->cur, buf, sizeof( float ) * elems ); if( err != SEGY_OK ) return err; } else { const size_t writec = fwrite( buf, sizeof( float ), elems, fp->fp ); if( writec != elems ) return SEGY_FWRITE_ERROR; } return SEGY_OK; } // step != 1, i.e. do strided reads int defstart = start < stop ? 0 : elems - 1; int slicelen = slicelength( start, stop, step ); if( fp->addr ) { /* if mmap is on, strided write is trivial and fast */ float* cur = (float*)fp->cur + defstart; for( ; slicelen > 0; cur += step, ++buf, --slicelen ) *cur = *buf; return SEGY_OK; } const int elemsize = elems * sizeof( float ); float* tracebuf = rangebuf ? rangebuf : malloc( elemsize ); // like in readsubtr, read a larger chunk and then step through that const size_t readc = fread( tracebuf, sizeof( float ), elems, fp->fp ); if( readc != elems ) { free( tracebuf ); return SEGY_FREAD_ERROR; } /* rewind, because fread advances the file pointer */ err = fseek( fp->fp, -elemsize, SEEK_CUR ); if( err != 0 ) { if( !rangebuf ) free( tracebuf ); return SEGY_FSEEK_ERROR; } float* cur = tracebuf + defstart; for( ; slicelen > 0; cur += step, --slicelen, ++buf ) *cur = *buf; const size_t writec = fwrite( tracebuf, sizeof( float ), elems, fp->fp ); if( !rangebuf ) free( tracebuf ); if( writec != elems ) return SEGY_FWRITE_ERROR; return SEGY_OK; } int segy_to_native( int format, long long size, float* buf ) { assert( sizeof( float ) == sizeof( uint32_t ) ); uint32_t u; for( long long i = 0; i < size; ++i ) { memcpy( &u, buf + i, sizeof( uint32_t ) ); u = ntohl( u ); memcpy( buf + i, &u, sizeof( uint32_t ) ); } if( format == SEGY_IBM_FLOAT_4_BYTE ) { for( long long i = 0; i < size; ++i ) ibm_native( buf + i ); } return SEGY_OK; } int segy_from_native( int format, long long size, float* buf ) { assert( sizeof( float ) == sizeof( uint32_t ) ); uint32_t u; if( format == SEGY_IBM_FLOAT_4_BYTE ) { for( long long i = 0; i < size; ++i ) native_ibm( buf + i ); } for( long long i = 0; i < size; ++i ) { memcpy( &u, buf + i, sizeof( uint32_t ) ); u = htonl( u ); memcpy( buf + i, &u, sizeof( uint32_t ) ); } return SEGY_OK; } /* * Determine the position of the element `x` in `xs`. * Returns -1 if the value cannot be found */ static int index_of( int x, const int* xs, int sz ) { for( int i = 0; i < sz; i++ ) { if( xs[i] == x ) return i; } return -1; } /* * Read the inline or crossline `lineno`. If it's an inline or crossline * depends on the parameters. The line has a length of `line_length` traces, * `offsets` are the number of offsets in this file, and `buf` must be of * (minimum) `line_length*samples_per_trace` size. Reads every `stride` trace, * starting at the trace specified by the *position* of the value `lineno` in * `linenos`. If `lineno` isn't present in `linenos`, SEGY_MISSING_LINE_INDEX * will be returned. * * If reading a trace fails, this function will return whatever error * segy_readtrace returns. */ int segy_read_line( segy_file* fp, int line_trace0, int line_length, int stride, int offsets, float* buf, long trace0, int trace_bsize ) { assert( sizeof( float ) == sizeof( int32_t ) ); assert( trace_bsize % 4 == 0 ); const int trace_data_size = trace_bsize / 4; stride *= offsets; for( ; line_length--; line_trace0 += stride, buf += trace_data_size ) { int err = segy_readtrace( fp, line_trace0, buf, trace0, trace_bsize ); if( err != 0 ) return err; } return SEGY_OK; } /* * Write the inline or crossline `lineno`. If it's an inline or crossline * depends on the parameters. The line has a length of `line_length` traces, * and `buf` must be of (minimum) `line_length*samples_per_trace` size. Reads * every `stride` trace, starting at the trace specified by the *position* of * the value `lineno` in `linenos`. If `lineno` isn't present in `linenos`, * SEGY_MISSING_LINE_INDEX will be returned. * * If reading a trace fails, this function will return whatever error * segy_readtrace returns. */ int segy_write_line( segy_file* fp, int line_trace0, int line_length, int stride, int offsets, const float* buf, long trace0, int trace_bsize ) { if( !fp->writable ) return SEGY_READONLY; assert( sizeof( float ) == sizeof( int32_t ) ); assert( trace_bsize % 4 == 0 ); const int trace_data_size = trace_bsize / 4; line_trace0 *= offsets; stride *= offsets; for( ; line_length--; line_trace0 += stride, buf += trace_data_size ) { int err = segy_writetrace( fp, line_trace0, buf, trace0, trace_bsize ); if( err != 0 ) return err; } return SEGY_OK; } int segy_line_trace0( int lineno, int line_length, int stride, int offsets, const int* linenos, int linenos_sz, int* traceno ) { int index = index_of( lineno, linenos, linenos_sz ); if( index < 0 ) return SEGY_MISSING_LINE_INDEX; if( stride == 1 ) index *= line_length; *traceno = index * offsets; return SEGY_OK; } int segy_inline_stride( int sorting, int inline_count, int* stride ) { switch( sorting ) { case SEGY_CROSSLINE_SORTING: *stride = inline_count; return SEGY_OK; case SEGY_INLINE_SORTING: *stride = 1; return SEGY_OK; default: return SEGY_INVALID_SORTING; } } int segy_crossline_stride( int sorting, int crossline_count, int* stride ) { switch( sorting ) { case SEGY_CROSSLINE_SORTING: *stride = 1; return SEGY_OK; case SEGY_INLINE_SORTING: *stride = crossline_count; return SEGY_OK; default: return SEGY_INVALID_SORTING; } } int segy_read_textheader( segy_file* fp, char *buf) { return segy_read_ext_textheader(fp, -1, buf ); } int segy_read_ext_textheader( segy_file* fp, int pos, char *buf) { if( pos < -1 ) return SEGY_INVALID_ARGS; if( !fp ) return SEGY_FSEEK_ERROR; const long offset = pos == -1 ? 0 : SEGY_TEXT_HEADER_SIZE + SEGY_BINARY_HEADER_SIZE + (pos * SEGY_TEXT_HEADER_SIZE); #ifdef HAVE_MMAP if ( fp->addr ) { encode( buf, (char*)fp->addr + offset, e2a, SEGY_TEXT_HEADER_SIZE ); buf[ SEGY_TEXT_HEADER_SIZE ] = '\0'; return SEGY_OK; } #endif //HAVE_MMAP int err = fseek( fp->fp, offset, SEEK_SET ); if( err != 0 ) return SEGY_FSEEK_ERROR; char localbuf[ SEGY_TEXT_HEADER_SIZE + 1 ] = { 0 }; const size_t read = fread( localbuf, 1, SEGY_TEXT_HEADER_SIZE, fp->fp ); if( read != SEGY_TEXT_HEADER_SIZE ) return SEGY_FREAD_ERROR; encode( buf, localbuf, e2a, SEGY_TEXT_HEADER_SIZE ); return SEGY_OK; } int segy_write_textheader( segy_file* fp, int pos, const char* buf ) { if( !fp->writable ) return SEGY_READONLY; int err; char mbuf[ SEGY_TEXT_HEADER_SIZE ]; if( pos < 0 ) return SEGY_INVALID_ARGS; err = encode( mbuf, buf, a2e, SEGY_TEXT_HEADER_SIZE ); if( err != 0 ) return err; const long offset = pos == 0 ? 0 : SEGY_TEXT_HEADER_SIZE + SEGY_BINARY_HEADER_SIZE + ((pos-1) * SEGY_TEXT_HEADER_SIZE); #ifdef HAVE_MMAP if( fp->addr ) { return memwrite( fp, (char*)fp->addr + offset, mbuf, SEGY_TEXT_HEADER_SIZE ); } #endif //HAVE_MMAP err = fseek( fp->fp, offset, SEEK_SET ); if( err != 0 ) return SEGY_FSEEK_ERROR; size_t writec = fwrite( mbuf, 1, SEGY_TEXT_HEADER_SIZE, fp->fp ); if( writec != SEGY_TEXT_HEADER_SIZE ) return SEGY_FWRITE_ERROR; return SEGY_OK; } int segy_textheader_size( void ) { return SEGY_TEXT_HEADER_SIZE + 1; } int segy_binheader_size( void ) { return SEGY_BINARY_HEADER_SIZE; } static int scaled_cdp( segy_file* fp, int traceno, float* cdpx, float* cdpy, long trace0, int trace_bsize ) { int32_t x, y, scalar; char trheader[ SEGY_TRACE_HEADER_SIZE ]; int err = segy_traceheader( fp, traceno, trheader, trace0, trace_bsize ); if( err != 0 ) return err; err = segy_get_field( trheader, SEGY_TR_CDP_X, &x ); if( err != 0 ) return err; err = segy_get_field( trheader, SEGY_TR_CDP_Y, &y ); if( err != 0 ) return err; err = segy_get_field( trheader, SEGY_TR_SOURCE_GROUP_SCALAR, &scalar ); if( err != 0 ) return err; float scale = scalar; if( scalar == 0 ) scale = 1.0; if( scalar < 0 ) scale = -1.0 / scale; *cdpx = x * scale; *cdpy = y * scale; return SEGY_OK; } int segy_rotation_cw( segy_file* fp, int line_length, int stride, int offsets, const int* linenos, int linenos_sz, float* rotation, long trace0, int trace_bsize ) { struct coord { float x, y; } nw, sw; int err; int traceno; err = segy_line_trace0( linenos[0], line_length, stride, offsets, linenos, linenos_sz, &traceno ); if( err != 0 ) return err; err = scaled_cdp( fp, traceno, &sw.x, &sw.y, trace0, trace_bsize ); if( err != 0 ) return err; /* read the last trace in the line */ traceno += (line_length - 1) * stride * offsets; err = scaled_cdp( fp, traceno, &nw.x, &nw.y, trace0, trace_bsize ); if( err != 0 ) return err; float x = nw.x - sw.x; float y = nw.y - sw.y; float radians = x || y ? atan2( x, y ) : 0; if( radians < 0 ) radians += 2 * acos(-1); *rotation = radians; return SEGY_OK; } segyio-1.5.2/applications/0000775000372000037200000000000013253720451015110 5ustar travistravissegyio-1.5.2/applications/apputils.h0000664000372000037200000000055113253720451017123 0ustar travistravis#ifndef SEGYIO_APPUTILS_H #define SEGYIO_APPUTILS_H int errmsg( int errcode, const char* msg ); int errmsg2( int errcode, const char* prelude, const char* msg ); int parseint( const char* str, int* x ); int bfield( const char* header, int field ); int trfield( const char* header, int field ); int printversion( const char* name ); #endif //SEGYIO_APPUTILS_H segyio-1.5.2/applications/segyinfo.c0000664000372000037200000001104213253720451017075 0ustar travistravis#include #include #include #include #include #include static void printSegyTraceInfo( const char* buf ) { int cdp, tsf, xl, il; segy_get_field( buf, SEGY_TR_ENSEMBLE, &cdp ); segy_get_field( buf, SEGY_TR_SEQ_FILE, &tsf ); segy_get_field( buf, SEGY_TR_CROSSLINE, &xl ); segy_get_field( buf, SEGY_TR_INLINE, &il ); printf("cdp: %d\n", cdp ); printf("TraceSequenceFile: %d\n", tsf ); printf("Crossline3D: %d\n", xl ); printf("Inline3D: %d\n", il ); } #define minimum(x,y) ((x) < (y) ? (x) : (y)) #define maximum(x,y) ((x) > (y) ? (x) : (y)) int main(int argc, char* argv[]) { if( argc < 2 ) { puts("Missing argument, expected run signature:"); printf(" %s [mmap]\n", argv[0]); exit(1); } segy_file* fp = segy_open( argv[ 1 ], "rb" ); if( !fp ) { perror( "fopen():" ); exit( 3 ); } if( argc > 2 && strcmp( argv[ 2 ], "mmap" ) == 0 ) { int err = segy_mmap( fp ); if( err != SEGY_OK ) fputs( "Could not mmap file. Using fstream fallback.", stderr ); } int err; char header[ SEGY_BINARY_HEADER_SIZE ]; err = segy_binheader( fp, header ); if( err != 0 ) { perror( "Unable to read segy binary header:" ); exit( err ); } const int format = segy_format( header ); const int samples = segy_samples( header ); const long trace0 = segy_trace0( header ); const int trace_bsize = segy_trace_bsize( samples ); int extended_headers; err = segy_get_bfield( header, SEGY_BIN_EXT_HEADERS, &extended_headers ); if( err != 0 ) { perror( "Can't read 'extended headers' field from binary header" ); exit( err ); } int traces; err = segy_traces( fp, &traces, trace0, trace_bsize ); if( err != 0 ) { perror( "Could not determine traces" ); exit( err ); } printf( "Sample format: %d\n", format ); printf( "Samples per trace: %d\n", samples ); printf( "Traces: %d\n", traces ); printf("Extended text header count: %d\n", extended_headers ); puts(""); char traceh[ SEGY_TRACE_HEADER_SIZE ]; err = segy_traceheader( fp, 0, traceh, trace0, trace_bsize ); if( err != 0 ) { perror( "Unable to read trace 0:" ); exit( err ); } puts("Info from first trace:"); printSegyTraceInfo( traceh ); err = segy_traceheader( fp, 1, traceh, trace0, trace_bsize ); if( err != 0 ) { perror( "Unable to read trace 1:" ); exit( err ); } puts(""); puts("Info from second trace:"); printSegyTraceInfo( traceh ); clock_t start = clock(); float* trbuf = malloc( sizeof( float ) * trace_bsize ); float minval = FLT_MAX; float maxval = FLT_MIN; int min_sample_count = 999999999; int max_sample_count = 0; for( int i = 0; i < traces; ++i ) { err = segy_traceheader( fp, i, traceh, trace0, trace_bsize ); if( err != 0 ) { perror( "Unable to read trace" ); exit( err ); } int sample_count; err = segy_get_field( traceh, SEGY_TR_SAMPLE_COUNT, &sample_count ); if( err != 0 ) { fprintf( stderr, "Invalid trace header field: %d\n", SEGY_TR_SAMPLE_COUNT ); exit( err ); } min_sample_count = minimum( sample_count, min_sample_count ); max_sample_count = maximum( sample_count, max_sample_count ); err = segy_readtrace( fp, i, trbuf, trace0, trace_bsize ); if( err != 0 ) { fprintf( stderr, "Unable to read trace: %d\n", i ); exit( err ); } segy_to_native( format, samples, trbuf ); for( int j = 0; j < samples; ++j ) { minval = minimum( trbuf[ j ], minval ); maxval = maximum( trbuf[ j ], maxval ); } } free( trbuf ); puts(""); puts("Info from last trace:"); err = segy_traceheader( fp, traces - 1, traceh, trace0, trace_bsize ); if( err != 0 ) { perror( "Unable to read trace." ); exit( err ); } printSegyTraceInfo( traceh ); puts(""); printf("Min sample count: %d\n", min_sample_count); printf("Max sample count: %d\n", max_sample_count); printf("Min sample value: %f\n", minval ); printf("Max sample value: %f\n", maxval ); puts(""); clock_t diff = clock() - start; printf("Read all trace headers in: %.2f s\n", (double) diff / CLOCKS_PER_SEC); segy_close( fp ); return 0; } segyio-1.5.2/applications/segyio-crop.c0000664000372000037200000003705413253720451017525 0ustar travistravis#include #include #include #include #include #include #include #include "apputils.c" #include static int help() { puts( "Usage: segyio-crop [OPTION]... SRC DST\n" "Copy a sub cube from SRC to DST\n" "\n" "-i, --iline-begin=LINE inline to copy from\n" "-I, --iline-end=LINE inline to copy to (inclusive)\n" "-x, --xline-begin=LINE crossline to copy from\n" "-X, --xline-end=LINE crossline to copy to (inclusive)\n" " --inline-begin alias to --iline-begin\n" " --crossline-begin alias to --xline-begin\n" "-s, --sample-begin=TIME measurement to copy from\n" "-S, --sample-end=TIME measurement to copy to (inclusive)\n" "-b, --il inline header word byte offset\n" "-B, --xl crossline header word byte offset\n" "-v, --verbose increase verbosity\n" " --version output version information and exit\n" " --help display this help and exit\n" "\n" "If no begin/end options are specified, this program is\n" "essentially a copy. If a begin option is omitted, the program\n" "copies from the start. If an end option is omitted, the program\n" "copies until the end.\n" ); return 0; } struct delay { int delay; int skip; int len; }; static struct delay delay_recording_time( const char* trheader, int sbeg, int send, int dt, int samples ) { long long t0 = trfield( trheader, SEGY_TR_DELAY_REC_TIME ); int trdt = trfield( trheader, SEGY_TR_SAMPLE_INTER ); if( trdt ) dt = trdt; /* * begin/end not specified - copy the full trace, so dont try to identify * the sub trace */ struct delay d = { t0, 0, samples }; if( sbeg < 0 && send == INT_MAX ) return d; /* determine what to cut off at the start of the trace */ if( sbeg - t0 > 0 ) { long long skip = ((sbeg - t0) * 1000) / dt; d.delay = t0 + ((skip * dt) / 1000 ); d.skip = skip; d.len -= d.skip; } /* determine what to cut off at the end of the trace */ if( (long long)send * 1000 < (t0 * 1000) + (samples * dt) ) { long long t0us = t0 * 1000; long long sendus = (long long)send * 1000; d.len -= (t0us + ((samples - 1) * dt) - sendus) / dt; } return d; } static int valid_trfield( int x ) { switch( x ) { case SEGY_TR_SEQ_LINE: case SEGY_TR_SEQ_FILE: case SEGY_TR_FIELD_RECORD: case SEGY_TR_NUMBER_ORIG_FIELD: case SEGY_TR_ENERGY_SOURCE_POINT: case SEGY_TR_ENSEMBLE: case SEGY_TR_NUM_IN_ENSEMBLE: case SEGY_TR_TRACE_ID: case SEGY_TR_SUMMED_TRACES: case SEGY_TR_STACKED_TRACES: case SEGY_TR_DATA_USE: case SEGY_TR_OFFSET: case SEGY_TR_RECV_GROUP_ELEV: case SEGY_TR_SOURCE_SURF_ELEV: case SEGY_TR_SOURCE_DEPTH: case SEGY_TR_RECV_DATUM_ELEV: case SEGY_TR_SOURCE_DATUM_ELEV: case SEGY_TR_SOURCE_WATER_DEPTH: case SEGY_TR_GROUP_WATER_DEPTH: case SEGY_TR_ELEV_SCALAR: case SEGY_TR_SOURCE_GROUP_SCALAR: case SEGY_TR_SOURCE_X: case SEGY_TR_SOURCE_Y: case SEGY_TR_GROUP_X: case SEGY_TR_GROUP_Y: case SEGY_TR_COORD_UNITS: case SEGY_TR_WEATHERING_VELO: case SEGY_TR_SUBWEATHERING_VELO: case SEGY_TR_SOURCE_UPHOLE_TIME: case SEGY_TR_GROUP_UPHOLE_TIME: case SEGY_TR_SOURCE_STATIC_CORR: case SEGY_TR_GROUP_STATIC_CORR: case SEGY_TR_TOT_STATIC_APPLIED: case SEGY_TR_LAG_A: case SEGY_TR_LAG_B: case SEGY_TR_DELAY_REC_TIME: case SEGY_TR_MUTE_TIME_START: case SEGY_TR_MUTE_TIME_END: case SEGY_TR_SAMPLE_COUNT: case SEGY_TR_SAMPLE_INTER: case SEGY_TR_GAIN_TYPE: case SEGY_TR_INSTR_GAIN_CONST: case SEGY_TR_INSTR_INIT_GAIN: case SEGY_TR_CORRELATED: case SEGY_TR_SWEEP_FREQ_START: case SEGY_TR_SWEEP_FREQ_END: case SEGY_TR_SWEEP_LENGTH: case SEGY_TR_SWEEP_TYPE: case SEGY_TR_SWEEP_TAPERLEN_START: case SEGY_TR_SWEEP_TAPERLEN_END: case SEGY_TR_TAPER_TYPE: case SEGY_TR_ALIAS_FILT_FREQ: case SEGY_TR_ALIAS_FILT_SLOPE: case SEGY_TR_NOTCH_FILT_FREQ: case SEGY_TR_NOTCH_FILT_SLOPE: case SEGY_TR_LOW_CUT_FREQ: case SEGY_TR_HIGH_CUT_FREQ: case SEGY_TR_LOW_CUT_SLOPE: case SEGY_TR_HIGH_CUT_SLOPE: case SEGY_TR_YEAR_DATA_REC: case SEGY_TR_DAY_OF_YEAR: case SEGY_TR_HOUR_OF_DAY: case SEGY_TR_MIN_OF_HOUR: case SEGY_TR_SEC_OF_MIN: case SEGY_TR_TIME_BASE_CODE: case SEGY_TR_WEIGHTING_FAC: case SEGY_TR_GEOPHONE_GROUP_ROLL1: case SEGY_TR_GEOPHONE_GROUP_FIRST: case SEGY_TR_GEOPHONE_GROUP_LAST: case SEGY_TR_GAP_SIZE: case SEGY_TR_OVER_TRAVEL: case SEGY_TR_CDP_X: case SEGY_TR_CDP_Y: case SEGY_TR_INLINE: case SEGY_TR_CROSSLINE: case SEGY_TR_SHOT_POINT: case SEGY_TR_SHOT_POINT_SCALAR: case SEGY_TR_MEASURE_UNIT: case SEGY_TR_TRANSDUCTION_MANT: case SEGY_TR_TRANSDUCTION_EXP: case SEGY_TR_TRANSDUCTION_UNIT: case SEGY_TR_DEVICE_ID: case SEGY_TR_SCALAR_TRACE_HEADER: case SEGY_TR_SOURCE_TYPE: case SEGY_TR_SOURCE_ENERGY_DIR_MANT: case SEGY_TR_SOURCE_ENERGY_DIR_EXP: case SEGY_TR_SOURCE_MEASURE_MANT: case SEGY_TR_SOURCE_MEASURE_EXP: case SEGY_TR_SOURCE_MEASURE_UNIT: case SEGY_TR_UNASSIGNED1: case SEGY_TR_UNASSIGNED2: return 1; default: return 0; } } #define TRHSIZE SEGY_TRACE_HEADER_SIZE #define BINSIZE SEGY_BINARY_HEADER_SIZE #define TEXTSIZE SEGY_TEXT_HEADER_SIZE struct options { int ibeg, iend; int xbeg, xend; int sbeg, send; int il, xl; char* src; char* dst; int verbosity; int version, help; const char* errmsg; }; static struct options parse_options( int argc, char** argv ) { struct options opts; opts.ibeg = -1, opts.iend = INT_MAX; opts.xbeg = -1, opts.xend = INT_MAX; opts.sbeg = -1, opts.send = INT_MAX; opts.il = SEGY_TR_INLINE, opts.xl = SEGY_TR_CROSSLINE; opts.verbosity = 0; opts.version = 0, opts.help = 0; opts.errmsg = NULL; struct options opthelp, optversion; opthelp.help = 1, opthelp.errmsg = NULL; optversion.version = 1, optversion.errmsg = NULL; static struct option long_options[] = { { "iline-begin", required_argument, 0, 'i' }, { "iline-end", required_argument, 0, 'I' }, { "inline-begin", required_argument, 0, 'i' }, { "inline-end", required_argument, 0, 'I' }, { "xline-begin", required_argument, 0, 'x' }, { "xline-end", required_argument, 0, 'X' }, { "crossline-begin", required_argument, 0, 'x' }, { "crossline-end", required_argument, 0, 'X' }, { "sample-begin", required_argument, 0, 's' }, { "sample-end", required_argument, 0, 'S' }, { "il", required_argument, 0, 'b' }, { "xl", required_argument, 0, 'B' }, { "verbose", no_argument, 0, 'v' }, { "version", no_argument, 0, 'V' }, { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0 } }; static const char* parsenum_errmsg[] = { "", "num must be an integer", "num must be non-negative" }; opterr = 1; while( true ) { int option_index = 0; int c = getopt_long( argc, argv, "vi:I:x:X:s:S:b:B:", long_options, &option_index ); if( c == -1 ) break; int ret; switch( c ) { case 0: break; case 'h': return opthelp; case 'V': return optversion; case 'v': ++opts.verbosity; break; case 'i': ret = parseint( optarg, &opts.ibeg ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'I': ret = parseint( optarg, &opts.iend ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'x': ret = parseint( optarg, &opts.xbeg ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'X': ret = parseint( optarg, &opts.xend ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 's': ret = parseint( optarg, &opts.sbeg ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'S': ret = parseint( optarg, &opts.send ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'b': ret = parseint( optarg, &opts.il ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; case 'B': ret = parseint( optarg, &opts.xl ); if( ret == 0 ) break; opts.errmsg = parsenum_errmsg[ ret ]; return opts; default: opthelp.errmsg = ""; return opthelp; } } if( argc - optind != 2 ) { errmsg( 0, "Wrong number of files" ); return opthelp; } opts.src = argv[ optind + 0 ]; opts.dst = argv[ optind + 1 ]; return opts; } int main( int argc, char** argv ) { struct options opts = parse_options( argc, argv ); if( opts.help ) exit( help() + (opts.errmsg ? 2 : 0) ); if( opts.version ) exit( printversion( "segyio-crop" ) ); if( opts.errmsg ) exit( errmsg( EINVAL, opts.errmsg ) ); int ibeg = opts.ibeg; int iend = opts.iend; int xbeg = opts.xbeg; int xend = opts.xend; int sbeg = opts.sbeg; int send = opts.send; int il = opts.il; int xl = opts.xl; int verbosity = opts.verbosity; if( !valid_trfield( il ) ) exit( errmsg( -3, "Invalid inline byte offset" ) ); if( !valid_trfield( xl ) ) exit( errmsg( -3, "Invalid crossline byte offset" ) ); if( ibeg > iend ) exit( errmsg( -4, "Invalid iline interval - file would be empty" ) ); if( xbeg > xend ) exit( errmsg( -4, "Invalid xline interval - file would be empty" ) ); if( sbeg > send ) exit( errmsg( -4, "Invalid sample interval - file would be empty" ) ); char textheader[ TEXTSIZE ] = { 0 }; char binheader[ BINSIZE ] = { 0 }; char trheader[ TEXTSIZE ] = { 0 }; FILE* src = fopen( opts.src, "rb" ); if( !src ) exit( errmsg2( errno, "Unable to open src", strerror( errno ) ) ); FILE* dst = fopen( opts.dst, "wb" ); if( !dst ) exit( errmsg2( errno, "Unable to open dst", strerror( errno ) ) ); /* copy the textual and binary headers */ if( verbosity > 0 ) puts( "Copying text header" ); int sz; sz = fread( textheader, TEXTSIZE, 1, src ); if( sz != 1 ) exit( errmsg2( errno, "Unable to read text header", strerror( errno ) ) ); sz = fwrite( textheader, TEXTSIZE, 1, dst ); if( sz != 1 ) exit( errmsg2( errno, "Unable to write text header", strerror( errno ) ) ); if( verbosity > 0 ) puts( "Copying binary header" ); sz = fread( binheader, BINSIZE, 1, src ); if( sz != 1 ) exit( errmsg2( errno, "Unable to read binary header", strerror( errno ) ) ); sz = fwrite( binheader, BINSIZE, 1, dst ); if( sz != 1 ) exit( errmsg2( errno, "Unable to write binary header", strerror( errno ) ) ); int ext_headers = bfield( binheader, SEGY_BIN_EXT_HEADERS ); if( ext_headers < 0 ) exit( errmsg( -1, "Malformed binary header" ) ); for( int i = 0; i < ext_headers; ++i ) { if( verbosity > 0 ) puts( "Copying extended text header" ); sz = fread( textheader, TEXTSIZE, 1, src ); if( sz != 1 ) exit( errmsg2( errno, "Unable to read ext text header", strerror( errno ) ) ); sz = fwrite( textheader, TEXTSIZE, 1, dst ); if( sz != 1 ) exit( errmsg2( errno, "Unable to write ext text header", strerror( errno ) ) ); } if( verbosity > 2 ) puts( "Computing samples-per-trace" ); const int bindt = bfield( binheader, SEGY_BIN_INTERVAL ); const int src_samples = bfield( binheader, SEGY_BIN_SAMPLES ); if( src_samples < 0 ) exit( errmsg( -2, "Could not determine samples per trace" ) ); if( verbosity > 2 ) printf( "Found %d samples per trace\n", src_samples ); float* trace = malloc( src_samples * sizeof( float ) ); if( verbosity > 0 ) puts( "Copying traces" ); long long traces = 0; while( true ) { sz = fread( trheader, TRHSIZE, 1, src ); if( sz != 1 && feof( src ) ) break; if( sz != 1 && ferror( src ) ) exit( errmsg( ferror( src ), "Unable to read trace header" ) ); int ilno = trfield( trheader, il ); int xlno = trfield( trheader, xl ); /* outside copy interval - skip this trace */ if( ilno < ibeg || ilno > iend || xlno < xbeg || xlno > xend ) { fseek( src, sizeof( float ) * src_samples, SEEK_CUR ); continue; } sz = fread( trace, sizeof( float ), src_samples, src ); if( sz != src_samples ) exit( errmsg2( errno, "Unable to read trace", strerror( errno ) ) ); /* figure out how to crop the trace */ struct delay d = delay_recording_time( trheader, sbeg, send, bindt, src_samples ); segy_set_field( trheader, SEGY_TR_DELAY_REC_TIME, d.delay ); segy_set_bfield( binheader, SEGY_BIN_SAMPLES, d.len ); if( verbosity > 2 ) printf( "Copying trace %lld\n", traces ); sz = fwrite( trheader, TRHSIZE, 1, dst ); if( sz != 1 ) exit( errmsg2( errno, "Unable to write trace header", strerror( errno ) ) ); sz = fwrite( trace + d.skip, sizeof( float ), d.len, dst ); if( sz != d.len ) exit( errmsg2( errno, "Unable to write trace", strerror( errno ) ) ); ++traces; } fseek( dst, SEGY_TEXT_HEADER_SIZE, SEEK_SET ); sz = fwrite( binheader, BINSIZE, 1, dst ); if( sz != 1 ) exit( errmsg2( errno, "Unable to write binary header", strerror( errno ) ) ); free( trace ); fclose( dst ); fclose( src ); } segyio-1.5.2/applications/segyio-catr.c0000664000372000037200000003644013253720451017511 0ustar travistravis#include #include #include #include #include #include #include #include "apputils.c" #define TRHSIZE SEGY_TRACE_HEADER_SIZE #define BINSIZE SEGY_BINARY_HEADER_SIZE static const int fields[] = { SEGY_TR_SEQ_LINE , SEGY_TR_SEQ_FILE , SEGY_TR_FIELD_RECORD , SEGY_TR_NUMBER_ORIG_FIELD , SEGY_TR_ENERGY_SOURCE_POINT , SEGY_TR_ENSEMBLE , SEGY_TR_NUM_IN_ENSEMBLE , SEGY_TR_TRACE_ID , SEGY_TR_SUMMED_TRACES , SEGY_TR_STACKED_TRACES , SEGY_TR_DATA_USE , SEGY_TR_OFFSET , SEGY_TR_RECV_GROUP_ELEV , SEGY_TR_SOURCE_SURF_ELEV , SEGY_TR_SOURCE_DEPTH , SEGY_TR_RECV_DATUM_ELEV , SEGY_TR_SOURCE_DATUM_ELEV , SEGY_TR_SOURCE_WATER_DEPTH , SEGY_TR_GROUP_WATER_DEPTH , SEGY_TR_ELEV_SCALAR , SEGY_TR_SOURCE_GROUP_SCALAR , SEGY_TR_SOURCE_X , SEGY_TR_SOURCE_Y , SEGY_TR_GROUP_X , SEGY_TR_GROUP_Y , SEGY_TR_COORD_UNITS , SEGY_TR_WEATHERING_VELO , SEGY_TR_SUBWEATHERING_VELO , SEGY_TR_SOURCE_UPHOLE_TIME , SEGY_TR_GROUP_UPHOLE_TIME , SEGY_TR_SOURCE_STATIC_CORR , SEGY_TR_GROUP_STATIC_CORR , SEGY_TR_TOT_STATIC_APPLIED , SEGY_TR_LAG_A , SEGY_TR_LAG_B , SEGY_TR_DELAY_REC_TIME , SEGY_TR_MUTE_TIME_START , SEGY_TR_MUTE_TIME_END , SEGY_TR_SAMPLE_COUNT , SEGY_TR_SAMPLE_INTER , SEGY_TR_GAIN_TYPE , SEGY_TR_INSTR_GAIN_CONST , SEGY_TR_INSTR_INIT_GAIN , SEGY_TR_CORRELATED , SEGY_TR_SWEEP_FREQ_START , SEGY_TR_SWEEP_FREQ_END , SEGY_TR_SWEEP_LENGTH , SEGY_TR_SWEEP_TYPE , SEGY_TR_SWEEP_TAPERLEN_START , SEGY_TR_SWEEP_TAPERLEN_END , SEGY_TR_TAPER_TYPE , SEGY_TR_ALIAS_FILT_FREQ , SEGY_TR_ALIAS_FILT_SLOPE , SEGY_TR_NOTCH_FILT_FREQ , SEGY_TR_NOTCH_FILT_SLOPE , SEGY_TR_LOW_CUT_FREQ , SEGY_TR_HIGH_CUT_FREQ , SEGY_TR_LOW_CUT_SLOPE , SEGY_TR_HIGH_CUT_SLOPE , SEGY_TR_YEAR_DATA_REC , SEGY_TR_DAY_OF_YEAR , SEGY_TR_HOUR_OF_DAY , SEGY_TR_MIN_OF_HOUR , SEGY_TR_SEC_OF_MIN , SEGY_TR_TIME_BASE_CODE , SEGY_TR_WEIGHTING_FAC , SEGY_TR_GEOPHONE_GROUP_ROLL1 , SEGY_TR_GEOPHONE_GROUP_FIRST , SEGY_TR_GEOPHONE_GROUP_LAST , SEGY_TR_GAP_SIZE , SEGY_TR_OVER_TRAVEL , SEGY_TR_CDP_X , SEGY_TR_CDP_Y , SEGY_TR_INLINE , SEGY_TR_CROSSLINE , SEGY_TR_SHOT_POINT , SEGY_TR_SHOT_POINT_SCALAR , SEGY_TR_MEASURE_UNIT , SEGY_TR_TRANSDUCTION_MANT , SEGY_TR_TRANSDUCTION_EXP , SEGY_TR_TRANSDUCTION_UNIT , SEGY_TR_DEVICE_ID , SEGY_TR_SCALAR_TRACE_HEADER , SEGY_TR_SOURCE_TYPE , SEGY_TR_SOURCE_ENERGY_DIR_MANT , SEGY_TR_SOURCE_ENERGY_DIR_EXP , SEGY_TR_SOURCE_MEASURE_MANT , SEGY_TR_SOURCE_MEASURE_EXP , SEGY_TR_SOURCE_MEASURE_UNIT , SEGY_TR_UNASSIGNED1 , SEGY_TR_UNASSIGNED2 }; static const char* su[91] = { "tracl" , "tracr" , "fldr" , "tracf" , "ep" , "cdp" , "cdpt" , "trid" , "nvs" , "nhs" , "duse" , "offset" , "gelev" , "selev" , "sdepth" , "gdel" , "sdel" , "swdep" , "gwdep" , "scalel" , "scalco" , "sx" , "sy" , "gx" , "gy" , "counit" , "wevel" , "swevel" , "sut" , "gut" , "sstat" , "gstat" , "tstat" , "laga" , "lagb" , "delrt" , "muts" , "mute" , "ns" , "dt" , "gain" , "igc" , "igi" , "corr" , "sfs" , "sfe" , "slen" , "styp" , "stat" , "stae" , "tatyp" , "afilf" , "afils" , "nofilf" , "nofils" , "lcf" , "hcf" , "lcs" , "hcs" , "year" , "day" , "hour" , "minute" , "sec" , "timbas" , "trwf" , "grnors" , "grnofr" , "grnlof" , "gaps" , "otrav" , "cdpx" , "cdpy" , "iline" , "xline" , "sp" , "scalsp" , "trunit" , "tdcm" , "tdcp" , "tdunit" , "triden" , "sctrh" , "stype" , "sedm" , "sede" , "smm" , "sme" , "smunit" , "uint1" , "uint2" }; static const char* segynames[91] = { "SEQ_LINE" , "SEQ_FILE" , "FIELD_RECORD" , "NUMBER_ORIG_FIELD" , "ENERGY_SOURCE_POINT" , "ENSEMBLE" , "NUM_IN_ENSEMBLE" , "TRACE_ID" , "SUMMED_TRACES" , "STACKED_TRACES" , "DATA_USE" , "OFFSET" , "RECV_GROUP_ELEV" , "SOURCE_SURF_ELEV" , "SOURCE_DEPTH" , "RECV_DATUM_ELEV" , "SOURCE_DATUM_ELEV" , "SOURCE_WATER_DEPTH" , "GROUP_WATER_DEPTH" , "ELEV_SCALAR" , "SOURCE_GROUP_SCALAR" , "SOURCE_X" , "SOURCE_Y" , "GROUP_X" , "GROUP_Y" , "COORD_UNITS" , "WEATHERING_VELO" , "SUBWEATHERING_VELO" , "SOURCE_UPHOLE_TIME" , "GROUP_UPHOLE_TIME" , "SOURCE_STATIC_CORR" , "GROUP_STATIC_CORR" , "TOT_STATIC_APPLIED" , "LAG_A" , "LAG_B" , "DELAY_REC_TIME" , "MUTE_TIME_START" , "MUTE_TIME_END" , "SAMPLE_COUNT" , "SAMPLE_INTER" , "GAIN_TYPE" , "INSTR_GAIN_CONST" , "INSTR_INIT_GAIN" , "CORRELATED" , "SWEEP_FREQ_START" , "SWEEP_FREQ_END" , "SWEEP_LENGTH" , "SWEEP_TYPE" , "SWEEP_TAPERLEN_START", "SWEEP_TAPERLEN_END" , "TAPER_TYPE" , "ALIAS_FILT_FREQ" , "ALIAS_FILT_SLOPE" , "NOTCH_FILT_FREQ" , "NOTCH_FILT_SLOPE" , "LOW_CUT_FREQ" , "HIGH_CUT_FREQ" , "LOW_CUT_SLOPE" , "HIGH_CUT_SLOPE" , "YEAR_DATA_REC" , "DAY_OF_YEAR" , "HOUR_OF_DAY" , "MIN_OF_HOUR" , "SEC_OF_MIN" , "TIME_BASE_CODE" , "WEIGHTING_FAC" , "GEOPHONE_GROUP_ROLL1", "GEOPHONE_GROUP_FIRST", "GEOPHONE_GROUP_LAST" , "GAP_SIZE" , "OVER_TRAVEL" , "CDP_X" , "CDP_Y" , "INLINE" , "CROSSLINE" , "SHOT_POINT" , "SHOT_POINT_SCALAR" , "MEASURE_UNIT" , "TRANSDUCTION_MANT" , "TRANSDUCTION_EXP" , "TRANSDUCTION_UNIT" , "DEVICE_ID" , "SCALAR_TRACE_HEADER" , "SOURCE_TYPE" , "SOURCE_ENERGY_DIR_MA", "SOURCE_ENERGY_DIR_EX", "SOURCE_MEASURE_MANT" , "SOURCE_MEASURE_EXP" , "SOURCE_MEASURE_UNIT" , "UNASSIGNED1" , "UNASSIGNED2" }; static int help() { puts( "Usage: segyio-catr [OPTION]... FILE\n" "Print specific trace headers from FILE\n" "\n" "-t, --trace=NUMBER trace to print\n" "-r, --range START STOP STEP range of traces to print\n" "-s, --strict fail on unreadable tracefields\n" "-S, --non-strict don't fail on unreadable tracefields\n" " this is the default behaviour\n" "-n, --segyio-names print with segyio tracefield names\n" "-v, --verbose increase verbosity\n" " --version output version information and exit\n" " --help display this help and exit\n" "\n" "the -r flag can takes up to three values: start, stop, step\n" "where all values are defaulted to zero\n" "flags -r and -t can be called multiple times\n" ); return 0; } typedef struct { int start, stop, step; } range; struct options { char* src; range* r; int rsize; int verbosity; int version, help; int strict, labels; const char* errmsg; }; enum { su_labels, segyio_labels } label_ids; static range fill_range( range r, int field, int val ) { switch( field ) { case 0: r.start = val; return r; case 1: r.stop = val; return r; case 2: r.step = val; return r; default: exit( 12 ); } } static struct options parse_options( int argc, char** argv ){ int rallocsize = 32; struct options opts; opts.rsize = 0; opts.r = calloc( sizeof( range ), rallocsize ); opts.verbosity = 0; opts.version = 0; opts.help = 0; opts.strict = 0; opts.labels = su_labels; opts.errmsg = NULL; static struct option long_options[] = { { "trace", required_argument, 0, 't' }, { "range", required_argument, 0, 'r' }, { "segyio", no_argument, 0, 'k' }, { "strict", no_argument, 0, 's' }, { "non-strict", no_argument, 0, 'S' }, { "verbose", no_argument, 0, 'v' }, { "version", no_argument, 0, 'V' }, { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0,} }; static const char* parsenum_errmsg[] = { "", "num must be an integer", "num must be non-negative" }; opterr = 1; while( true ) { int option_index = 0; int c = getopt_long( argc, argv, "sSkvt:r:", long_options, &option_index ); if( c == -1 ) break; int ret; switch( c ) { case 0: break; case 'h': opts.help = 1; return opts; case 'V': opts.version = 1; return opts; case 'v': ++opts.verbosity; break; case 's': opts.strict = 1; break; case 'S': opts.strict = 0; break; case 'k': opts.labels = segyio_labels; break; case 'r': // intentional fallthrough case 't': if( opts.rsize == rallocsize ) { rallocsize *= 2; range* re = realloc( opts.r, rallocsize*sizeof( range ) ); if( !re ) { opts.errmsg = "Unable to reallocate memory"; return opts; } opts.r = re; } range* r = opts.r + opts.rsize; r->start = r->stop = r->step = 0; if( c == 't' ) { ret = parseint( optarg, &r->start ); if( ret ) { opts.errmsg = parsenum_errmsg[ ret ]; return opts; } goto done; } ret = sscanf(optarg," %d %d %d", &r->start, &r->stop, &r->step ); if( ret && ( r->start < 0 || r->stop < 0 || r->step < 0 ) ) { opts.errmsg = "range parameters must be positive"; return opts; } // if sscanf found something it consumes 1 argument, and we // won't have to rewind optind if( !ret ) optind--; for( int pos = ret; optind < argc && pos < 3; pos++, optind++ ) { int val; ret = parseint( argv[ optind ], &val ); /* parseint returns 1 when the string contains more than an * int (or not an int at all) we assume that the remaining * range parameters were defaulted and give control back to * argument parsing * * such invocations include: * segyio-catr -r 1 foo.sgy * segyio-catr -r 1 2 -s foo.sgy */ if( ret == 0 ) *r = fill_range( *r, pos, val ); if( ret == 1 ) goto done; if( ret == 2 ) { opts.errmsg = parsenum_errmsg[ ret ]; return opts; } } done: ++opts.rsize; break; default: opts.help = 1; opts.errmsg = ""; return opts; } } if( argc - optind != 1 ) { errmsg( 0, "Wrong number of files" ); opts.errmsg = ""; return opts; } opts.src = argv[ optind ]; if( opts.rsize == 0 ) opts.rsize = 1; return opts; } int main( int argc, char** argv ) { struct options opts = parse_options( argc, argv ); if( opts.help ) exit( help() ); if( opts.version ) exit( printversion( "segyio-catr" ) ); if( opts.errmsg ) exit( errmsg( EINVAL, opts.errmsg ) ); int strict = opts.strict; const char** labels; switch( opts.labels ) { case su_labels: labels = su; break; case segyio_labels: labels = segynames; break; default: labels = su; break; } for( range* r = opts.r; r < opts.r + opts.rsize; ++r ) { if( r->start > r->stop && strict ) exit( errmsg( -3, "Range is empty" ) ); } char trheader[ TRHSIZE ]; char binheader[ BINSIZE ]; segy_file* src = segy_open( opts.src, "r" ); if( !src ) exit( errmsg2( errno, "Unable to open src", strerror( errno ) ) ); int err = segy_binheader( src, binheader ); if( err ) exit( errmsg( errno, "Unable to read binheader" ) ); int samnr = segy_samples( binheader ); int trace_bsize = segy_trace_bsize( samnr ); long trace0 = segy_trace0( binheader ); int numtrh; err = segy_traces( src, &numtrh, trace0, trace_bsize ); if( err ) exit( errmsg( errno, "Unable to determine number of traces in file" ) ); for( range* r = opts.r; r < opts.r + opts.rsize; ++r ) { if( r->stop == 0 ) r->stop = r->start; if( r->step == 0 ) r->step = 1; } for( range* r = opts.r; r < opts.r + opts.rsize; ++r ) { for( int i = r->start; i <= r->stop; i += r->step ) { if( i > numtrh && strict ) exit( errmsg2( errno, "Unable to read traceheader", "out of range" ) ); if( i > numtrh ) break; err = segy_traceheader( src, i - 1, trheader, trace0, trace_bsize ); if( err ) exit( errmsg( errno, "Unable to read trace header" ) ); for( int j = 0; j < 91; j++ ) { int f; segy_get_field( trheader, fields[j], &f ); printf( "%s\t%d\n", labels[j], f ); } } } free( opts.r ); segy_close( src ); return 0; } segyio-1.5.2/applications/test/0000775000372000037200000000000013253720451016067 5ustar travistravissegyio-1.5.2/applications/test/cath.output0000664000372000037200000000625013253720451020273 0ustar travistravisC 1 DATE: 2016-09-19 C 2 AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE C 3 Written by libsegyio (python) C 4 C 5 C 6 C 7 C 8 C 9 C10 C11 TRACE HEADER POSITION: C12 INLINE BYTES 189-193 | OFFSET BYTES 037-041 C13 CROSSLINE BYTES 193-197 | C14 C15 END EBCDIC HEADER C16 C17 C18 C19 C20 C21 C22 C23 C24 C25 C26 C27 C28 C29 C30 C31 C32 C33 C34 C35 C36 C37 C38 C39 C40 segyio-1.5.2/applications/test/catb.output0000664000372000037200000000034513253720451020264 0ustar travistravisjobid 0 lino 0 reno 0 ntrpr 25 nart 0 hdt 4000 dto 0 hns 50 nso 0 format 1 fold 0 tsort 0 vscode 0 hsfs 0 hsfe 0 hslen 0 hstyp 0 schn 0 hstas 0 hstae 0 htatyp 0 hcorr 0 bgrcv 0 rcvm 0 mfeet 0 polyt 0 vpol 0 rev 0 trflag 0 exth 0 segyio-1.5.2/applications/test/catr.output0000664000372000037200000001104113253720451020277 0ustar travistravistracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 1 xline 23 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 1 xline 24 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 2 xline 20 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 2 xline 21 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 2 xline 22 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 2 xline 23 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 tracl 0 tracr 0 fldr 0 tracf 0 ep 0 cdp 0 cdpt 0 trid 0 nvs 0 nhs 0 duse 0 offset 1 gelev 0 selev 0 sdepth 0 gdel 0 sdel 0 swdep 0 gwdep 0 scalel 0 scalco 0 sx 0 sy 0 gx 0 gy 0 counit 0 wevel 0 swevel 0 sut 0 gut 0 sstat 0 gstat 0 tstat 0 laga 0 lagb 0 delrt 0 muts 0 mute 0 ns 0 dt 0 gain 0 igc 0 igi 0 corr 0 sfs 0 sfe 0 slen 0 styp 0 stat 0 stae 0 tatyp 0 afilf 0 afils 0 nofilf 0 nofils 0 lcf 0 hcf 0 lcs 0 hcs 0 year 0 day 0 hour 0 minute 0 sec 0 timbas 0 trwf 0 grnors 0 grnofr 0 grnlof 0 gaps 0 otrav 0 cdpx 0 cdpy 0 iline 3 xline 21 sp 0 scalsp 0 trunit 0 tdcm 0 tdcp 0 tdunit 0 triden 0 sctrh 0 stype 0 sedm 0 sede 0 smm 0 sme 0 smunit 0 uint1 0 uint2 0 segyio-1.5.2/applications/CMakeLists.txt0000664000372000037200000000740313253720451017654 0ustar travistravisproject(segyio-apps) if( NOT HAVE_GETOPT_H OR NOT HAVE_GETOPT_LONG ) message(WARNING "Could not find getopt. Not building applications.") return () endif () add_library(apputils STATIC apputils.c) target_link_libraries(apputils segyio) target_compile_options(apputils BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) target_compile_definitions(apputils PUBLIC -Dsegyio_MAJOR=${segyio_MAJOR} -Dsegyio_MINOR=${segyio_MINOR} ) add_executable(segyinfo segyinfo.c) target_link_libraries(segyinfo segyio) target_compile_options(segyinfo BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) add_executable(segyinspect segyinspect.c) target_link_libraries(segyinspect segyio) target_compile_options(segyinspect BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) add_executable(segyio-cath segyio-cath.c) target_link_libraries(segyio-cath segyio apputils) target_compile_options(segyio-cath BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) add_executable(segyio-catb segyio-catb.c) target_link_libraries(segyio-catb segyio apputils) target_compile_options(segyio-catb BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) add_executable(segyio-catr segyio-catr.c) target_link_libraries(segyio-catr segyio apputils) target_compile_options(segyio-catr BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) add_executable(segyio-crop segyio-crop.c) target_link_libraries(segyio-crop segyio apputils) target_compile_options(segyio-crop BEFORE PRIVATE $<$:${warnings-c}> ${c99} ) install(TARGETS segyio-cath segyio-catb segyio-catr segyio-crop DESTINATION ${CMAKE_INSTALL_BINDIR}) if (NOT BUILD_TESTING) return () endif () set(small ${testdata}/small.sgy) set(test ${CMAKE_CURRENT_SOURCE_DIR}/test) add_test(NAME catr.arg.t COMMAND segyio-catr -t 5 ${small}) add_test(NAME catr.arg.r1 COMMAND segyio-catr -r 5 ${small}) add_test(NAME catr.arg.r2 COMMAND segyio-catr -r 1 2 ${small}) add_test(NAME catr.arg.r3 COMMAND segyio-catr -r 1 2 3 ${small} -r 5 6) add_test(NAME catr.arg.multiple COMMAND segyio-catr "-r 1" 5 2 ${small} -r 1 6) add_test(NAME catr.arg.rt COMMAND segyio-catr -r 1 2 ${small} -t 5) add_test(NAME catb.arg.help COMMAND segyio-catb --help) add_test(NAME catb.fail.nosegy COMMAND segyio-catb ${test}/catb.output) add_test(NAME catb.fail.nofile COMMAND segyio-catb not-exist) add_test(NAME catb.fail.noarg COMMAND segyio-catb) add_test(NAME cath.arg.help COMMAND segyio-cath --help) add_test(NAME cath.fail.nosegy COMMAND segyio-cath ${test}/cath.output) add_test(NAME cath.fail.nofile COMMAND segyio-cath --strict not-exist) add_test(NAME cath.fail.noarg COMMAND segyio-cath) set_tests_properties(catb.fail.nosegy catb.fail.nofile catb.fail.noarg cath.fail.nosegy cath.fail.nofile cath.fail.noarg PROPERTIES WILL_FAIL ON) add_custom_target(test-app-output ALL DEPENDS catb.out cath.out catr.out) add_custom_command( OUTPUT catb.out cath.out catr.out COMMENT "running applications for output comparison" DEPENDS segyio-catb segyio-cath segyio-catr test/catb.output test/cath.output test/catr.output COMMAND segyio-catb ${small} > catb.out COMMAND segyio-cath ${small} > cath.out COMMAND segyio-catr -r 4 9 -t 12 ${small} > catr.out ) add_test(NAME catb.output COMMAND ${CMAKE_COMMAND} -E compare_files ${test}/catb.output catb.out ) add_test(NAME cath.output COMMAND ${CMAKE_COMMAND} -E compare_files ${test}/cath.output cath.out ) add_test(NAME catr.output COMMAND ${CMAKE_COMMAND} -E compare_files ${test}/catr.output catr.out ) segyio-1.5.2/applications/segyio-cath.c0000664000372000037200000001206013253720451017467 0ustar travistravis#include #include #include #include #include #include #include "apputils.c" #include static int help() { puts( "Usage: segyio-cath [OPTION]... [FILE]...\n" "Concatenate the textual header(s) from FILE(s) to standard output.\n" "\n" "-n, --num the textual header to show, starts at 0\n" "-a, --all all textual headers\n" "-s, --strict abort if a header or file is not found\n" " primarily meant for shell scripts\n" "-S, --nonstrict ignore missing headers\n" " this is the default behaviour\n" " --version output version information and exit\n" " --help display this help and exit\n" "\n" "By default, only the non-extended header is printed, which is\n" "equivalent to --num 0\n" ); return 0; } static int ext_headers( segy_file* fp ) { char binary[ SEGY_BINARY_HEADER_SIZE ]; int err = segy_binheader( fp, binary ); if( err ) return -1; int32_t ext; err = segy_get_bfield( binary, SEGY_BIN_EXT_HEADERS, &ext ); if( err ) return -2; return ext; } static void print_header( const char* header ) { for( int line = 0, ch = 0; line < 40; ++line ) { for( int c = 0; c < 80; ++c, ++ch ) putchar( header[ ch ] ); putchar( '\n' ); } } int main( int argc, char** argv ) { static int all = false; static int strict = false; static int version = 0; static struct option long_options[] = { { "num", required_argument, 0, 'n' }, { "all", no_argument, &all, 1 }, { "strict", no_argument, &strict, 1 }, { "nonstrict", no_argument, &strict, 0 }, { "version", no_argument, &version, 1 }, { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0 } }; int num_alloc_sz = 32; int* num = calloc( sizeof( int ), num_alloc_sz ); int num_sz = 0; while( true ) { int option_index = 0; int c = getopt_long( argc, argv, "n:asS", long_options, &option_index ); if( c == -1 ) break; char* endptr; switch( c ) { case 0: break; case 'h': exit( help() ); case 's': strict = 1; break; case 'S': strict = 0; break; case 'a': all = 1; break; case 'n': if( version ) break; if( num_sz == num_alloc_sz - 1 ) { num_alloc_sz *= 2; int* re = realloc( num, num_alloc_sz * sizeof( int ) ); if( !re ) exit( errmsg( errno, "Unable to alloc" ) ); num = re; } num[ num_sz ] = strtol( optarg, &endptr, 10 ); if( *endptr != '\0' ) exit( errmsg( EINVAL, "num must be an integer" ) ); if( num[ num_sz ] < 0 ) exit( errmsg( EINVAL, "num must be non-negative" ) ); break; default: exit( help() ); } } if( version ) exit( printversion( "segyio-cath" ) ); char header[ SEGY_TEXT_HEADER_SIZE + 1 ] = { 0 }; if( argc - optind < 1 ) exit( errmsg( 2, "missing file operand\n" "Try 'segyio-cath --help' for more information." ) ); for( int i = optind; i < argc; ++i ) { segy_file* fp = segy_open( argv[ i ], "r" ); if( !fp ) fprintf( stderr, "segyio-cath: %s: No such file or directory\n", argv[ i ] ); if( !fp && strict ) exit( errmsg( 2, NULL ) ); if( !fp ) continue; if( num_sz == 0 ) num_sz = 1; const int exts = ext_headers( fp ); if( exts < 0 ) exit( errmsg( 1, "Unable to read binary header" ) ); if( all ) { /* just create the list 0,1,2... as if it was passed explicitly */ if( exts >= num_alloc_sz ) { num_alloc_sz = exts * 2; int* re = realloc( num, num_alloc_sz * sizeof( int ) ); if( !re ) exit( errmsg( errno, "Unable to alloc" ) ); num = re; } num_sz = exts + 1; num[ 0 ] = 0; for( int j = 0; j < exts; ++j ) num[ j + 1 ] = j; } for( int j = 0; j < num_sz; ++j ) { if( strict && ( num[ j ] < 0 || num[ j ] > exts ) ) exit( errmsg( EINVAL, "Header index out of range" ) ); if( num[ j ] < 0 || num[ j ] > exts ) continue; int err = num[ j ] == 0 ? segy_read_textheader( fp, header ) : segy_read_ext_textheader( fp, num[ j ] - 1, header ); if( err != 0 ) exit( errmsg( errno, "Unable to read header" ) ); print_header( header ); } segy_close( fp ); } free( num ); return 0; } segyio-1.5.2/applications/segyio-catb.c0000664000372000037200000001002713253720451017462 0ustar travistravis#include #include #include #include #include #include #include "apputils.h" #include static int printhelp(){ puts( "Usage: segyio-catb [OPTION]... [FILE]...\n" "Concatenate the binary header from FILE(s) to seismic unix " "output.\n" "\n" "--version output version information and exit\n" "--help display this help and exit\n" "\n" ); return 0; } static int get_binary_value( char* binheader, int bfield ){ int32_t f; segy_get_bfield( binheader, bfield, &f ); return f; } struct options { int version, help; const char* errmsg; }; static struct options parse_options( int argc, char** argv ){ struct options opts; opts.version = 0, opts.help = 0; opts.errmsg = NULL; struct options opthelp, optversion; opthelp.version = 0, opthelp.help = 1, opthelp.errmsg = NULL; optversion.version = 1, optversion.help = 0, optversion.errmsg = NULL; static struct option long_options[] = { {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; opterr = 1; while( true ){ int option_index = 0; int c = getopt_long( argc, argv, "", long_options, &option_index); if ( c == -1 ) break; switch( c ){ case 0: break; case 'h': return opthelp; case 'V': return optversion; default: opthelp.errmsg = ""; return opthelp; } } return opts; } int main( int argc, char** argv ){ static const char* su[ 30 ] = { "jobid", "lino", "reno", "ntrpr", "nart", "hdt", "dto", "hns", "nso", "format", "fold", "tsort", "vscode", "hsfs", "hsfe", "hslen", "hstyp", "schn", "hstas", "hstae", "htatyp", "hcorr", "bgrcv", "rcvm", "mfeet", "polyt", "vpol", "rev", "trflag", "exth" }; static int bfield_value[ 30 ] = { SEGY_BIN_JOB_ID, SEGY_BIN_LINE_NUMBER, SEGY_BIN_REEL_NUMBER, SEGY_BIN_TRACES, SEGY_BIN_AUX_TRACES, SEGY_BIN_INTERVAL, SEGY_BIN_INTERVAL_ORIG, SEGY_BIN_SAMPLES, SEGY_BIN_SAMPLES_ORIG, SEGY_BIN_FORMAT, SEGY_BIN_ENSEMBLE_FOLD, SEGY_BIN_SORTING_CODE, SEGY_BIN_VERTICAL_SUM, SEGY_BIN_SWEEP_FREQ_START, SEGY_BIN_SWEEP_FREQ_END, SEGY_BIN_SWEEP_LENGTH, SEGY_BIN_SWEEP, SEGY_BIN_SWEEP_CHANNEL, SEGY_BIN_SWEEP_TAPER_START, SEGY_BIN_SWEEP_TAPER_END, SEGY_BIN_TAPER, SEGY_BIN_CORRELATED_TRACES, SEGY_BIN_BIN_GAIN_RECOVERY, SEGY_BIN_AMPLITUDE_RECOVERY, SEGY_BIN_MEASUREMENT_SYSTEM, SEGY_BIN_IMPULSE_POLARITY, SEGY_BIN_VIBRATORY_POLARITY, SEGY_BIN_SEGY_REVISION, SEGY_BIN_TRACE_FLAG, SEGY_BIN_EXT_HEADERS }; if( argc == 1 ){ int err = errmsg(2, "Missing argument\n"); printhelp(); return err; } struct options opts = parse_options( argc, argv ); if( opts.help ) return printhelp(); if( opts.version ) return printversion( "segyio-catb" ); for( int i = optind; i < argc; ++i ){ segy_file* fp = segy_open( argv[ i ], "r" ); if( !fp ) return errmsg(opterr, "No such file or directory"); char binheader[ SEGY_BINARY_HEADER_SIZE ]; int err = segy_binheader( fp, binheader ); if( err ) return errmsg(opterr, "Unable to read binary header"); for( int c = 0; c < 30; ++c ){ printf( "%s\t%d\n", su[ c ], get_binary_value( binheader, bfield_value[ c ] )); } segy_close( fp ); } return 0; } segyio-1.5.2/applications/apputils.c0000664000372000037200000000214413253720451017116 0ustar travistravis#include #include #include #include "apputils.h" #include int errmsg( int errcode, const char* msg ) { if( !msg ) return errcode; fputs( msg, stderr ); fputc( '\n', stderr ); return errcode; } int errmsg2( int errcode, const char* prelude, const char* msg ) { if( !prelude ) return errmsg( errcode, msg ); fputs( prelude, stderr ); fputc( ':', stderr ); fputc( ' ', stderr ); return errmsg( errcode, msg ); } int parseint( const char* str, int* x ) { char* endptr; *x = strtol( str, &endptr, 10 ); if( *endptr != '\0' ) return 1; if( *x < 0 ) return 2; return 0; } int bfield( const char* header, int field ) { int32_t f; int err = segy_get_bfield( header, field, &f ); if( err ) return -1; return f; } int trfield( const char* header, int field ) { int32_t f; int err = segy_get_field( header, field, &f ); if( err ) return -1; return f; } int printversion( const char* name ) { printf( "%s (segyio version %d.%d)\n", name, segyio_MAJOR, segyio_MINOR ); return 0; } segyio-1.5.2/applications/segyinspect.c0000664000372000037200000001240713253720451017615 0ustar travistravis#include #include #include #include #include #include static const char* getSampleFormatName( int format ) { switch( format ) { case SEGY_IBM_FLOAT_4_BYTE: return "IBM Float"; case SEGY_SIGNED_INTEGER_4_BYTE: return "Int 32"; case SEGY_SIGNED_SHORT_2_BYTE: return "Int 16"; case SEGY_FIXED_POINT_WITH_GAIN_4_BYTE: return "Fixed Point with gain (Obsolete)"; case SEGY_IEEE_FLOAT_4_BYTE: return "IEEE Float"; case SEGY_NOT_IN_USE_1: return "Not in Use 1"; case SEGY_NOT_IN_USE_2: return "Not in Use 2"; case SEGY_SIGNED_CHAR_1_BYTE: return "Int 8"; default: return "Unknown"; } } static const char* getFastestDirectionName( int sorting ) { if ( sorting == SEGY_CROSSLINE_SORTING) { return "CROSSLINE"; } else { return "INLINE_SORTING"; } } int main(int argc, char* argv[]) { int err; if( argc < 2 ) { puts("Missing argument, expected run signature:"); printf(" %s [mmap] [INLINE_BYTE CROSSLINE_BYTE]\n", argv[0]); printf(" Inline and crossline bytes default to: 189 and 193\n"); exit(1); } int xl_field = SEGY_TR_CROSSLINE; int il_field = SEGY_TR_INLINE; bool memory_map = argc > 2 && strcmp( argv[ 2 ], "mmap" ) == 0; if( ( memory_map && argc > 4 ) || ( !memory_map && argc > 2 ) ) { int argindex = memory_map ? 2 : 3; il_field = atoi(argv[ argindex + 0 ]); xl_field = atoi(argv[ argindex + 1 ]); } clock_t start = clock(); segy_file* fp = segy_open( argv[ 1 ], "rb" ); if( !fp ) { perror( "fopen()" ); exit( SEGY_FOPEN_ERROR ); } if( memory_map ) { err = segy_mmap( fp ); if( err != SEGY_OK ) fputs( "Could not mmap file. Using fstream fallback.", stderr ); } char header[ SEGY_BINARY_HEADER_SIZE ]; err = segy_binheader( fp, header ); if( err != 0 ) { perror( "Unable to read segy binary header" ); exit( err ); } const int format = segy_format( header ); const int samples = segy_samples( header ); const long trace0 = segy_trace0( header ); const int trace_bsize = segy_trace_bsize( samples ); int traces; err = segy_traces( fp, &traces, trace0, trace_bsize ); if( err != 0 ) { perror( "Could not determine traces" ); exit( err ); } int sorting; err = segy_sorting( fp, il_field, xl_field, SEGY_TR_OFFSET, &sorting, trace0, trace_bsize ); if( err != 0 ) { perror( "Could not determine sorting" ); exit( err ); } int offsets; err = segy_offsets( fp, il_field, xl_field, traces, &offsets, trace0, trace_bsize ); if( err != 0 ) { perror( "Could not determine offsets" ); exit( err ); } int inline_count, crossline_count; if( sorting == SEGY_INLINE_SORTING ) { err = segy_count_lines( fp, xl_field, offsets, &inline_count, &crossline_count, trace0, trace_bsize ); } else { err = segy_count_lines( fp, il_field, offsets, &crossline_count, &inline_count, trace0, trace_bsize ); } if( err != 0 ) { fprintf( stderr, "Errcode %d\n", err ); perror( "Could not count lines" ); exit( err ); } int* inline_indices = malloc( sizeof( int ) * inline_count ); int* crossline_indices = malloc( sizeof( int ) * crossline_count ); err = segy_inline_indices( fp, il_field, sorting, inline_count, crossline_count, offsets, inline_indices, trace0, trace_bsize ); if( err != 0 ) { perror( "Could not determine inline numbers" ); exit( err ); } err = segy_crossline_indices( fp, xl_field, sorting, inline_count, crossline_count, offsets, crossline_indices, trace0, trace_bsize ); if( err != 0 ) { fprintf( stderr, "Errcode %d\n", err ); perror( "Could not determine crossline numbers" ); exit( err ); } clock_t diff = clock() - start; printf( "Crosslines..........: %d\n", crossline_count); printf( "Inlines.............: %d\n", inline_count); printf( "Offsets.............: %d\n", offsets); printf( "Samples.............: %d\n", samples); printf( "Sample format.......: %s\n", getSampleFormatName( format ) ); printf( "Fastest direction...: %s\n", getFastestDirectionName( sorting ) ); puts(""); puts("Crossline indexes:"); for( int i = 0; i < crossline_count; i++ ) { printf( "%d ", crossline_indices[i] ); } puts("\n"); puts("Inline indexes:"); for( int i = 0; i < inline_count; i++ ) { printf( "%d ", inline_indices[i] ); } puts("\n"); puts("Sample indexes:"); //for (int i = 0; i < spec->sample_count; i++) { // printf("%.2f ", spec->sample_indexes[i]); //} puts("\n"); printf("Inspection took : %.2f s\n", (double) diff / CLOCKS_PER_SEC); free( inline_indices ); free( crossline_indices ); segy_close( fp ); exit(0); } segyio-1.5.2/cppcheck/0000775000372000037200000000000013253720451014202 5ustar travistravissegyio-1.5.2/cppcheck/segyio.cfg0000664000372000037200000000343513253720451016167 0ustar travistravis false false false false false false segy_open segy_close segyio-1.5.2/cppcheck/suppressions.txt0000664000372000037200000000045213253720451017521 0ustar travistravispreprocessorErrorDirective:*pyconfig.h preprocessorErrorDirective:*Python.h // Ignore all warnings for external libraries *:*external/* // cppcheck struggles with the expression templates of catch, giving this error // for long == long comparisons compareBoolExpressionWithInt:*lib/test/segy.cpp segyio-1.5.2/breaking-changes.md0000664000372000037200000000330613253720451016136 0ustar travistravis# Planned breaking changes for segyio 2 This documents describe the planned breaking changes for segyio >= 2. ## libsegyio ### removal of linkage of ebcdic2ascii and friends, and ibm2ieee The conversion functions are linkable today, but the symbols are not exposed in any headers. Giving these functions public linkage (for testing purposes) was a mistake, but hopefully no users will be affected as the symbols never appears in the header file. Affected functions: - `ebcdic2ascii` - `ascii2ebcdic` - `ibm2ieee` - `ieee2ibm` - `segy_seek` - `segy_ftell` ## python ### accessing closed files raises ValueError Calling methods on closed files should not raise `IOError`, but `ValueError`, in order to be uniform with python's own file object. Most users shouldn't (or wouldn't) use this error for control flow or recovery, but in order to account for it, the change is postponed. ### f.text no longer implicitly resolves to f.text[0] The implicit accessing of the mandatory text header is inconsistent and error prone, and won't work in segyio2. In segyio2, the only way to grab the mandatory text header is `f.text[0]` ### str(f.text[n]) removed The implicit string conversion should be either tied to fmt or other explicit methods, instead of implicitly resolving into a newline-interleaved 80-linewidth string ### f.text[n] always returns 3200 bytes buffer object Currently, the returned buffer assumes the contents are somewhat string-like, and has some wonky behaviour when there are 0-bytes in the text headers, and not proper whitespace paddings. In segyio2, this behaviour will be reviewed for consistency, so that 0-bytes-in-buffers are handled gracefully, and textheaders can be indexed into reliably and safely. segyio-1.5.2/test-data/0000775000372000037200000000000013253720451014310 5ustar travistravissegyio-1.5.2/test-data/1x1.sgy0000664000372000037200000001116013253720451015444 0ustar travistravis@@@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  <Ŭ==u=)k=4m=> =If{=S=^_1BdBdBdBdBdBdBdBdBdBdBBBBBBBBBBCCCCCCCCCCsegyio-1.5.2/test-data/right-small.sgy0000664000372000037200000003441013253720451017261 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2dA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55dA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*dAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA dAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA AdA AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA cA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55cA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*cA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# cA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#cA# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# bA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355bA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*bA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 bA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3bA3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 aAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55aAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+aACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC aACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACaAC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC `AS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55`AS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+`ASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS `ASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS AS`AS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/1xN.sgy0000664000372000037200000002412013253720451015501 0ustar travistravis@@@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  <Ŭ==u=)k=4m=> =If{=S=^_1BdBdBdBdBdBdBdBdBdBdBBBBBBBBBBCCCCCCCCCC?(?)>?) ?)7?)?)*0?)4?)?)?)I?)T!BdBdBdBdBdBdBdBdBdBdBBBBBBBBBBC)C)C)C)C)C)C)C)C)C)?Q?Q?R}?R ?Rv?R?R*o?R4?R?h?RIBdBdBd Bd Bd!Bd"Bd"Bd#Bd$Bd$BBB B!B!B"B#B#B$B%CQCRCRCRCRCRCRCRCRCR?zG?z?z@?{?{ 9?{?{ 1?{*?{5*?{?BdBdBdBdBdBdBdBdBdBdBBBBBBBBBBCzCzC{C{C{C{C{C{C{C{? ????? x?? p?*?5iBd =Bd >Bd ?Bd ?Bd @Bd @Bd ABd BBd BBd CB =B >B ?B ?B @B AB AB BB CB CCCCCCCCCCC??I??B??;? ?3? ?+,Bd Bd Bd Bd Bd Bd Bd Bd Bd Bd B B B B B B B B B B CCCCCCCCCCsegyio-1.5.2/test-data/normal-small.sgy0000664000372000037200000003441013253720451017434 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2 A33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55 A\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^* AA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA  AAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA A A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA  A#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55 A#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^* A#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A#  A#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A# A# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A#  A333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355 A3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^* A3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3  A3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3 A3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3  AC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55 AC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+ ACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC  ACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC AC AC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC  AS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55 AS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+ ASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS  ASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS AS AS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/Mx1.sgy0000664000372000037200000002412013253720451015500 0ustar travistravis@@@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  <Ŭ==u=)k=4m=> =If{=S=^_1BdBdBdBdBdBdBdBdBdBdBBBBBBBBBBCCCCCCCCCCAA AAA*A4A>AIASA^BeBeBeBeBeBeBeBeBeBeBBBBBBBBBBCCCCCCCCCCA A A A A *A 4A ?A IA TA ^BfBfBfBfBfBfBfBfBfBfBBBBBBBBBBCCCCCCCCCCA0A0 A0A0A0*A04A0?A0IA0TA0^BgBgBgBgBgBgBgBgBgBgBBBBBBBBBBCCCCCCCCCCA@A@ A@A@A@*A@4A@?A@IA@TA@^BhBhBhBhBhBhBhBhBhBhBBBBBBBBBBCCCCCCCCCCAPAP APAPAP*AP4AP?APIAPTAP^BiBiBiBiBiBiBiBiBiBiBBBBBBBBBBCCCCCCCCCCsegyio-1.5.2/test-data/text.sgy0000664000372000037200000000702013253720451016017 0ustar travistravis@@z@aa@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@k@@z@@k@@z@@@@@@@@@@@@@@@@@@z@~M`]@M@]k~M`]@M@]@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@K@k@z@@@@@@@@@@@@@@@@@@@@@@@@@z@@k@@k@`@Kk@`@K@@@@@@@@@@@@z@@k@@k@`@Kk@`@K@@@@@@@@@@@z@@k@@k@`@Kk@`@K@@@@@@@@@@z@@k@@k@`@Kk@`@K@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@`@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ segyio-1.5.2/test-data/obtuse-small.sgy0000664000372000037200000003441013253720451017445 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2ddA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55ecA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*fbAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA gaAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA Ah`A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA ccA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55dbA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*eaA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# f`A#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#g_A# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# bbA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355caA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*d`A3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 e_A3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3f^A3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 aaAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55b`AC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+c_ACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC d^ACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACe]AC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC ``AS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55a_AS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+b^ASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS c]ASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASd\AS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/small.sgy0000664000372000037200000003441013253720451016146 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2A33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55A\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*AA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA AAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA AA AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA A#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55A#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*A#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# A#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# A333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355A3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*A3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 A3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 AC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55AC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+ACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC ACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC AS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55AS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+ASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS ASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/straight-small.sgy0000664000372000037200000003441013253720451017771 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2ddA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55dcA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*dbAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA daAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA Ad`A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA cdA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55ccA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*cbA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# caA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#c`A# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# bdA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355bcA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*bbA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 baA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3b`A3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 adAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55acAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+abACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC aaACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACa`AC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC `dAS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55`cAS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+`bASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS `aASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS AS``AS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/left-small.sgy0000664000372000037200000003441013253720451017076 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2dA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55cA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*bAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA aAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA A`A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA dA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55cA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*bA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# aA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#`A# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# dA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355cA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*bA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 aA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3`A3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 dAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55cAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+bACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC aACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC AC`AC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC dAS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55cAS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+bASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS aASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS AS`AS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/small-ps.sgy0000664000372000037200000002412013253720451016563 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  BeBeBeBeBeBeBeBeBeBeBBBBBBBBBBBeBeBe Be Be!Be"Be"Be#Be$Be$BBB B!B!B"B#B#B$B%BeBeBeBeBeBeBeBeBeBeBBBBBBBBBBBfBfBfBfBfBfBfBfBfBfBBBBBBBBBBBfBfBf Bf Bf!Bf"Bf"Bf#Bf$Bf$BBB B!B!B"B#B#B$B%BfBfBfBfBfBfBfBfBfBfBBBBBBBBBBBgBgBgBgBgBgBgBgBgBgBBBBBBBBBBBgBgBg Bg Bg!Bg"Bg"Bg#Bg$Bg$BBB B!B!B"B#B#B$B%BgBgBgBgBgBgBgBgBgBgBBBBBBBBBBBhBhBhBhBhBhBhBhBhBhBBBBBBBBBBBhBhBh Bh Bh!Bh"Bh"Bh#Bh$Bh$BBB B!B!B"B#B#B$B%BhBhBhBhBhBhBhBhBhBhBBBBBBBBBBsegyio-1.5.2/test-data/reflex-small.sgy0000664000372000037200000003441013253720451017431 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2ddA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55ccA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*bbAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA aaAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA A``A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA ceA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55bdA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*acA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# `bA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#_aA# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# bfA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355aeA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*`dA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 _cA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3^bA3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 agAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55`fAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+_eACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC ^dACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC AC]cAC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC `hAS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55_gAS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+^fASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS ]eASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS AS\dAS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/acute-small.sgy0000664000372000037200000003441013253720451017245 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2dA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55eA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*fAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA gAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA AhA AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA cA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55dA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*eA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# fA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#gA# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# bA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355cA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*dA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 eA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3fA3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 aAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55bAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+cACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC dACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACeAC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC `AS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55aAS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+bASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS cASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASdAS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/test-data/inv-acute-small.sgy0000664000372000037200000003441013253720451020037 0ustar travistravis@@z@``@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@晉@@@M]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@@@@j@@@`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`@j@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2ddA33A3=A3HA3RA3]A3gA3rA3|A3A3A3A3A3A3A3A3A3A3A3A3A4A4A4A4$A4.A49A4CA4NA4XA4cA4mA4xA4A4A4A4A4A4A4A4A4A4A4A4A5A5 A5A5 A5*A55dcA\)A\3A\=A\HA\RA\]A\gA\rA\|A\A\A\A\A\A\A\A\A\A\A\A\A]A]A]A]$A]/A]9A]DA]NA]YA]cA]nA]xA]A]A]A]A]A]A]A]A]A]A]A]A^A^ A^A^ A^*dbAA)A3A>AHASA]AhArA}AAAAAAAAAAAAAAAA$A/A9ADANAYAcAnAxAAAAAAAAAAAAAA AA daAAA)A4A>AHASA]AhArA}AAAAAAAAAAAAAAAA%A/A:ADAOAYAdAnAyAAAAAAAAAAAAAA Ad`A AAA)A4A>AIASA^AhAsA}A׈AגAםAקAײA׼AAAAAAAAAA%A/A:ADAOAYAdAnAyA؃A؎AؘAأAحAظAAAAAAAA deA#33A#3=A#3HA#3RA#3]A#3gA#3rA#3|A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#3A#4A#4A#4A#4$A#4.A#49A#4CA#4NA#4XA#4cA#4mA#4xA#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#4A#5A#5 A#5A#5 A#5*A#55ddA#\)A#\3A#\>A#\HA#\SA#\]A#\hA#\rA#\|A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#\A#]A#]A#]A#]$A#]/A#]9A#]DA#]NA#]YA#]cA#]nA#]xA#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#]A#^A#^ A#^A#^ A#^*dcA#A#)A#3A#>A#HA#SA#]A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#$A#/A#9A#DA#NA#YA#cA#nA#xA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#A# dbA#A#A#)A#4A#>A#IA#SA#^A#hA#rA#}A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#A#A#A#A#A#A#A#A#A#A#A#A#A# A#daA# A#A#A#)A#4A#>A#IA#SA#^A#hA#sA#}A#׈A#גA#םA#קA#ײA#׼A#A#A#A#A#A#A#A#A#A#%A#/A#:A#DA#OA#YA#dA#nA#yA#؃A#؎A#ؘA#أA#حA#ظA#A#A#A#A#A#A#A# dfA333A33=A33HA33RA33]A33gA33rA33|A33A33A33A33A33A33A33A33A33A33A33A33A34A34A34A34$A34.A349A34CA34NA34XA34cA34mA34xA34A34A34A34A34A34A34A34A34A34A34A34A35A35 A35A35 A35*A355deA3\)A3\3A3\>A3\HA3\SA3\]A3\hA3\rA3\|A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3\A3]A3]A3]A3]$A3]/A3]9A3]DA3]NA3]YA3]cA3]nA3]xA3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3]A3^A3^ A3^A3^ A3^*ddA3A3)A33A3>A3HA3SA3]A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3$A3/A39A3DA3NA3YA3cA3nA3xA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3A3 dcA3A3A3)A34A3>A3IA3SA3^A3hA3rA3}A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3A3A3A3A3A3A3A3A3A3A3A3A3A3 A3dbA3 A3A3A3)A34A3>A3IA3SA3^A3hA3sA3}A3׈A3גA3םA3קA3ײA3׼A3A3A3A3A3A3A3A3A3A3%A3/A3:A3DA3OA3YA3dA3nA3yA3؃A3؎A3ؘA3أA3حA3ظA3A3A3A3A3A3A3A3 dgAC33AC3=AC3HAC3RAC3]AC3gAC3rAC3|AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC3AC4AC4AC4AC4$AC4/AC49AC4DAC4NAC4YAC4cAC4nAC4xAC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC4AC5AC5 AC5AC5 AC5*AC55dfAC\)AC\3AC\>AC\HAC\SAC\]AC\hAC\rAC\}AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC\AC]AC]AC]AC]$AC]/AC]9AC]DAC]NAC]YAC]cAC]nAC]xAC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC]AC^AC^ AC^AC^ AC^+deACAC)AC3AC>ACHACSAC]AChACrAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACAC ddACACAC)AC4AC>ACIACSAC^AChACsAC}ACACACACACACACACACACACACACACACAC%AC/AC:ACDACOACYACdACnACyACACACACACACACACACACACACACAC ACdcAC ACACAC)AC4AC>ACIACSAC^AChACsAC}AC׈ACגACםACקACײAC׼ACACACACACACACACACAC%AC0AC:ACEACOACZACdACoACyAC؃AC؎ACؘACأACحACظACACACACACACACAC dhAS33AS3=AS3HAS3RAS3]AS3gAS3rAS3|AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS3AS4AS4AS4AS4$AS4/AS49AS4DAS4NAS4YAS4cAS4nAS4xAS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS4AS5AS5 AS5AS5 AS5*AS55dgAS\)AS\3AS\>AS\HAS\SAS\]AS\hAS\rAS\}AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS\AS]AS]AS]AS]$AS]/AS]9AS]DAS]NAS]YAS]cAS]nAS]xAS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS]AS^AS^ AS^AS^ AS^+dfASAS)AS3AS>ASHASSAS]AShASrAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASAS deASASAS)AS4AS>ASIASSAS^AShASsAS}ASASASASASASASASASASASASASASASAS%AS/AS:ASDASOASYASdASnASyASASASASASASASASASASASASASAS ASddAS ASASAS)AS4AS>ASIASSAS^AShASsAS}AS׈ASגASםASקASײAS׼ASASASASASASASASASAS%AS0AS:ASEASOASZASdASoASyAS؃AS؎ASؘASأASحASظASASASASASASASAS segyio-1.5.2/changelog.md0000664000372000037200000002200113253720451014666 0ustar travistravis# 1.5.2 * `open` and `create` handle anything string-convertible as filename argument * pytest replaces unittest, both as library and test driver * segyio-crop now respects the byte-offset arguments, instead of just ignoring them and using 189 & 193 from SEG-Y revision 1 * Some errors in readme and documentation is cleared up * Fixed a bug in create that would trigger 16-bit integer overflow, effectively breaking any file with more than 65k traces. # 1.5.0 * A bug making an external text header disappear has been fixed * The python extension has been changed to use C++ features, simplifying code and dropping the use of capsules * segyio-cath sets non-zero status code on failures * Application testing is moved from python to cmake, giving a large speedup * The IndexError message when accessing headers out-of-range has been improved * Some work has been moved from python into the extension * Error messages in python have received an overhaul * Errors produced when memory-mapping files are made consistent with fstream sourced errors # 1.4.0 * segyio has learned how to resample a file (`segyio.tools.resample`). This function does not actually touch the data traces, but rewrites the header fields and attributes required to persistently change sample rate. Interpolation of data traces must be done manually, but for a strict reinterpretation this function is sufficient * segyio has learned to read enough structure from a file to create a new file with the same dimensions and lines (`segyio.tools.metadata`) * segyio has learned to create unstructured files (only traces, no inlines or crosslines) * `f.text[0] =` requires `bytes` convertability. This catches some errors that were previously fatal or silent * `f.text` broken internal buffer allocations fixed * `f.text[n] =` supports strings longer or shorter than 3200 bytes by truncating or padding, respectively * Fixed a bug where a particular length of mode strings caused errors * `segyio.open('w')` raises an exception, instead of silently truncating the file and failing later when the file size does not match the expected. * `segy_traces` now fails if `trace0` is outside domain, instead of silently returning garbage * Return correct size for dirty, newly-created files. This means carefully created new files can be `mmap`d earlier * Methods on closed files always raise exceptions * `mmap` support improved - all C functions are `mmap` aware. * The file is now closed after a successful `mmap` call * `str.format` used for string interpolation over the `%` operator * Several potential issues found by static analysis, such as non-initialised temporaries, divide-by-zero code paths, and leak-errors (in process teardown) addressed, to reduce noise and improve safety * Error message on failure in `segyio.tools.dt` improved * Error message on unparsable global binary header improved changes for the next major release * Catch2 is introduced to test the core C library, replacing the old `test/segy.c` family of tests * Contract for `segy_traces` clarified in documentation * Docstrings improved for `depth_slice` and `segyio.create` * A new document, `breaking-changes.md`, lists planned deprecations and API * The readme has gotten a makeover, with better structure, an index, and more examples * `setup.py` requires setuptools >= 28. A rather recent setuptools was always a requirement, but not codified * scan-build (clang analysis) enabled on Travis # 1.3.9 * Fix OS X wheel packaging. # 1.3.8 * Automate python ast analysis with bandit on travis * The installed python extension is built without rpath * The numpy minimum requirement is handled in setup.py * The python installation layout can be configured via cmake e.g. -DPYTHON_INSTALL_LAYOUT=deb # 1.3.7 * Makefiles can turn off version detection from git from env or via args # 1.3.6 * Applications no longer spuriously ignore arguments * All assertClose calls in tests have non-zero epsilon # 1.3.5 * make install respects DESTDIR, also for python files # 1.3.4 * Reading a slice in gather mode is significantly faster * Use ftello when available to support large files on systems where long is 32-bit * The python extension is changed to use C++; a C++ compiler is now required, not just optional, when building the python extension * Many internal and infrastructure improvements * The python library is built with setuptools - still integrated with cmake. Users building from source can still do cmake && make * Git tag is now authority on version numbers, as opposed to the version string recorded in the cmake file. * General building and infrastructure improvements # 1.3.3 * Infrastructure fixes # 1.3.2 * Add test for segy-cath * Fix memory double-free error in subtr functions # 1.3.1 * Fix a typo in segyio-crop --version * Some building improvements # 1.3.0 * segyio is now meant to be used as proper versions, not trunk checkouts. changelogs from now on will be written when new versions are released, not on a monthly basis. * Minor typo fixes in segyio-cath help * Applications now come with man pages * `header` modes handle negative indexing * `header.update` handle any key-value iterable * New application: segyio-catr for printing trace headers * New application: segyio-catb for printing the binary header * New application: segyio-crop for copying sub cubes # 2017.06 * seismic unix-style aliases are available for python in the segyio.su namespace * segyio has learned how to calculate the rotation of the cube (segy_rotation_cw and segyio.tools.rotation) * The python header objects behave more as dicts as expected * The new program segyio-cath is added, a cat-like program that concatenate text headers. * Infrastructure improvements * segyio for python is available via pypi (pip) * segyio is now meant to be consumed with binary downloads of versions, but with a rapid release cycle. Releases within a major release will be backwards compatible * Shared linking of the python extension is considered deprecated on Windows. # 2017.05 * Requirements for the shape of the right hand side of `f.trace[:] =` expressions are relaxed and accepts more inputs * C interface slightly cleaned up for C99 compliance * C library can reason on arbitrary header words for offsets, not just 37 # 2017.04 * Examples in the readme * Delay recording time (t0) is interpreted as milliseconds, not microseconds * Some minor optimisations * segy_mmap is more exception safe * The applications warn if mmap fails * Support for static analysis with cppcheck * The statoil/pycmake repo is used for python integration in cmake * Minor milli/microsecond bugfixes in mex bindings * tools.wrap added for printing textual headers to screen or file # 2017.03 * Float conversions (ibm <-> native) has been optimised and is much faster * `segy_binheader_size` returns signed int like its friends * `sample_interval` steps are now floats * Multiple internal bug fixes * Some buffer leaks are plugged * segyio has learned to deal with files without good geometry. If `strict = False` is passed to segyio.open, and a file is without well-sorted inlines/crosslines, open will return a file handle, but with geometry-dependent modes disabled * `file.samples` returns a list of samples, not number of samples * Readme has been improved. * `trace[int]` is more robust w.r.t. inputs * A new mode has been added; gather. gather depends on a good geometry, and its getitem `[il, xl, slice(offsets)]` returns all offsets for an inline/crossline intersection # 2017.02 * segyio has learned to deal with large files (>4G) on more platforms * segyio can read quickly attributes (trace header words) over the full file * python can tell the fast and slow directions apart * Reading depth slices is much faster * tools.collect for gathering read samples into a single numpy ndarray * tools.cube for easily reading a full cube * tools.native for fast third-party segy-to-native float conversion * File opening in binary mode is now enforced * Data types have been overhauled (prefer signed integers) * Enumerations have been SEGY prefixed to reduce collisions * Building shared libs can be switched on/off on cmake invocation * Makefiles and CI overhauls # 2017.01 * Matlab has learned about prestack files * Reading traces in matlab no longer fails when not reading the whole file * Matlab argument keys have been renamed # 2016.11 * Fixed some condtions where a failed write would corrupt trace data * Fixed a memory leak bug * VERSION string added to python * Experimental memory-mapped file support * Line-oriented C functions are offset aware * Python offset property exposes offset numbers, not just count * Support for pre-stack files, with new subindexing syntax, line[n, offset] * Improved python repl (shell, read-eval-print-loop) support * The widgets have color- and layout selectors # 2016.10 * Matlab tests can optionally be turned off * The application Segyviewer is embeddable and provided by the segyview sub library * libcwrap has been replaced fully by the python C api * OS X and experimental Windows support * A new sub mode for traces, raw, for eager reading of trace data