diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2015-07-08 20:46:52 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2015-07-08 20:46:52 -0400 |
commit | fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch) | |
tree | bdda434976bc09c864f2e4fa6f16ba1952b1e555 /sys/qpoe | |
download | iraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz |
Initial commit
Diffstat (limited to 'sys/qpoe')
135 files changed, 20355 insertions, 0 deletions
diff --git a/sys/qpoe/QPDEFS b/sys/qpoe/QPDEFS new file mode 100644 index 00000000..863337fd --- /dev/null +++ b/sys/qpoe/QPDEFS @@ -0,0 +1,60 @@ +# QPDEFS -- SAMPLE global macro definition file for QPOE. This should be +# modified (and a copy placed somewhere outside the QPOE sources) to establish +# the defaults for a given site. Each user may also have their own private +# copy of this file. The environment variable `qmfiles' defines the list of +# QPDEFS files to be read to define the runtime environment for QPOE. + +# Interface parameters (defaults shown). +#set bucketlen 1024 +#set cachesize 8 +#set indexlen 100 +#set maxlfiles 128 +#set pagesize 512 +#set sbufsize 2048 +#set stablen 2048 +#set progbuflen 1024 +#set databuflen 4096 +#set maxfrlutlen 8192 +#set maxrrlutlen 1024 +#set lutminranges 5 +#set lutscale 15 +#set maxpushback 8192 +#set blockfactor 8 +#set optbufsize 524288 +#set debuglevel 0 + +set lutscale 32 +set blockfactor 4 + +# The event structure used in qpoe$zzdebug.x for s/w testing. +define evfields {s:x,s:y,s,s,d,s,s} +define x s0 +define y s2 +define pha s4 +define pi s6 +define time d8 +define dx s16 +define dy s18 + +# An alias. +define t time + +# Some test filters. +define box x=(400:800),y=(200:400) +define dbox dx=(400:800),dy=(200:400) +define eventimes (30:31,32:33,34:35,36:37,38:39,40:41,42:43) +define oddtimes (31:32,33:34,35:36,37:38,39:40,41:42,43:44) +define alltimes (eventimes,oddtimes) + +# The following is for integer LUT tests; a bitwise test would be more +# suitable for testing for an even integer. + +define even (2,4,6,8,10,12,14,16,18,20,22,24,26,28,30) + +# Test argument substitution in macros. +define eq $1=($2) +define ne $1=!($2) +define le $1=(:$2) +define lt $1=(!($2:)) +define ge $1=($2:) +define gt $1=(!(:$2)) diff --git a/sys/qpoe/QPOE.hlp b/sys/qpoe/QPOE.hlp new file mode 100644 index 00000000..cbb0ee5b --- /dev/null +++ b/sys/qpoe/QPOE.hlp @@ -0,0 +1,1201 @@ +.help QPOE Jun90 "Quick POE Design" +.ce +\fBQuick-POE (Position Ordered Event File) Interface Design\fR +.ce +Doug Tody +.ce +July, 1988 + +.NH +Introduction + + The POE (Position Ordered Event file) facility is an interface and file +structure used to store and access the event (photon) lists generated +by event counting detectors. Each event is described by a unique position, +time, energy, and possibly other parameters (e.g., polarization, position in +other coordinate systems, or instrument related parameters). In the case of +an imaging event counting detector, the "image" generated consists of this list +of discrete events, rather than the regular matrix produced by a conventional +sampling detector. Both types of detectors are fundamental to astronomy. + +The POE interface is a stand alone interface built upon the standard VOS +interfaces DFIO (in a future release), PLIO, SYMTAB, FIO, and other lower +level interfaces. The POE interface may be called directly by applications +code to create and access POE datafiles, for event file specific processing. +In addition, an IMIO image kernel is provided so that POE files may be +accessed as (read only) images, allowing existing IRAF image tasks to be +used to access POE files. The main function of the POE image kernel is to +filter and sample the event list in real time, returning a conventional +sampled grid (image matrix) to the high level applications code. +The parameters controlling the filtering and sampling operations may be +specified by the user when the image (POE file) is accessed, making runtime +filtering of events possible in connection with any general image processing +task. + +.NH 2 +Important Concepts + + The primary object dealt with by this interface is the \fIevent file\fR, +consisting of a free format \fIfile header\fR and the main \fIevent list\fR. +The event list is a collection of \fIevent structures\fR, e.g., photons +hitting an imaging detector during the period of observation recorded by the +event file. Each event is characterized by a standard set of attributes such +as the position of the event in detector, sky, or other coordinates, the time +at which the event was recorded, the energy of the event, and so on, plus +optionally additional instrument dependent attributes (in general the event +structure cannot be fixed, and the nomenclature may vary depending upon the +science being performed). + +The events may appear in the event list in any order, but since most access +to image data tends to be spatial in nature, access will be most efficient if +the event list is position ordered. This is the convention chosen by QPOE +and hence the name \fIpoe\fRfile, or \fIp\fRosition \fIo\fRrdered \fIe\fRvent +file. An important alternative ordering is time ordering, which preserves +the order in which the events were originally recorded, but which requires +a complete scan of the event list to accumulate the events in a region of +interest during analysis. There are cases where time ordering might be +preferable to position ordering, e.g., for time series analysis of a long +observation. + +Of fundamental importance to the analysis of data from event counting +detectors is the concept of \fIfiltering\fR. It is in the nature of event +counting detectors that they are often used to observe very faint objects +for very long periods of integration. The total amount of data (number of +events) may be limited, so one wants to preserve all events, but since the +quality of the data may vary both with time and with position, it is common +to want to reject a portion of the data. Conversely, the analysis being +performed may require one to examine only a portion of the data, e.g., only +events with a certain range of energies or arrival times, occurring within a +given region of the image. Often the analysis will be repeated many times +wtih different filters. + +Hence, most analysis of event counting data typically involves both +\fIrejection\fR and \fIregion of interest\fR filtering. Rejection filtering +depends mostly upon the data itself, hence the rejection filter for an image +is a part of the image and should be in effect by default whenever the image +is accessed, although we would like to physically record all data and be able +to change the rejection filter either temporarily or indefinitely if desired. +Region of interest filtering, on the other hand, depends upon the scientific +analysis being performed rather than upon the data, hence is highly variable +and should be controlled by the user, independently of the data. + +.NH 2 +Interface Requirements + + Given the description of the problem to be solved presented in the previous +section, we can make the following observations regarding the POE interface: +.ls 4 o +A flexible binary file header supporting both scalar and variable length vector +fields is essential. Examples of vector fields include the aspect and temporal +records (actually arrays of records, or subtables), the processing history +(probably stored as a single variable length text buffer), and the rejection +and region of interest filters (PLIO external format byte sequences stored as +opaque binary arrays). +.le +.ls o +In the general case the details of the event structure depend upon the +instrument for which data is being stored. The minimum requirement is that +the event structure consist of a set of standard fields (x, y, time, energy) +followed by a variable length, instrument dependent area (hence the size of +the event structure, while fixed for a given datafile, should be allowed to +vary depending upon the data). Ideally all fields should be named and +accessible for filtering, the names chosen should be variables rather than +constants, and the set of fields used to describe an event should be allowed +to vary depending upon the data. +.le +.ls o +Runtime access to the event list, including event-attribute and spatial +filtering, should be as efficient as possible since this is likely to be +by far the most time consuming part of the interface. Header access +efficiency is much less important and is not expected to be a problem. +.le +.ls o +For most efficient access the event list should be stored sorted upon some +primary key, with an index maintained by the interface for that key, +and used for efficient retrieval. The minimum requirement is that the primary +or sort key be the Y coordinate (corresponding to image lines). Ideally it +should be possible for the sort key to be any field of the event structure, +or any combination of fields (e.g., Y+X, or T). Ideally the interface itself +should be responsible for maintaining the event list in sort order; this is +not a requirement since writing to event files is much less common than +reading. Ideally it should be possible for the event list to be unordered, +and it should be possible to transparently access the event list regardless +of the ordering. +.le +.ls o +Rejection filtering is typically required by the data, yet we wish to retain +all the data and be able to override or replace the default rejection mask. +The rejection filter is logically associated with the data and should be +stored with the data. The minimum requirement is for the interface to be able +to store all the data plus the rejection mask, and be able to return only +the "good" data at runtime. The interface is not required to perform rejection +filtering at runtime, although it would be desirable to be able to do so if +the efficiency penalty were not too great. Alternatively, the event list +could be prefiltered, and the "good" and "bad" events stored in different +places in the datafile, requiring that the entire file be rebuilt to change +the rejection filter. +.le +.ls o +Region of interest filtering is a common operation for event data, and can be +difficult to implement efficiently, hence should be supported directly by the +interface. At a minimum it should be possible to filter events by defining a +range of acceptable or unacceptable values for each of some subset of the event +attributes (e.g., energy or time). Ideally it should also be possible to +specify an arbitrarily long list of ranges of acceptable values. Ideally +spatial filtering should be supported as well; this is required for rejection +filtering, but is at most a desirable option for spatial region of interest +filtering (there is nothing about spatial filtering which is unique to event +data, hence it might be more appropriate to implement it at a higher level, +but on the other hand it might be more efficient to implement it at the event +i/o level since the event list is position ordered and can be very large). +.le +.ls o +It should be possible to specify the various filtering options both +transparently to applications programs (via a symbolic expression passed +into the interface by the user as part of the file specification), +or procedurally, via \fIset\fR-parameter calls issued by the client program. +.le + +The above issues need to be adequately addressed in order to have a useful +interface. In the longer term there are many other considerations, e.g., +it is also desirable for the data format to be machine independent, and the +data format should be flexible, to accommodate the inevitable evolutionary +revisions as well as to accommodate data from a variety of instruments or +projects. The event files can be very large, hence efficiency is a major +consideration for event i/o and filtering. + +.NH +The QPOE Interface +.NH 2 +Implementation Strategy + + A two step operation is planned for implementing the POE interface within +IRAF. The first step is the so called quick-POE interface. The objective of +quick-POE is to provide the necessary functionality so that applications +development can proceed immediately, without waiting for the general interface +to be developed. Once quick-POE is in place development of the fully general +POE interface can proceed at a more leisurely pace consistent with the plan to +provide most of the functionality of the generalized POE interface with other +standard IRAF facilities currently under development, most notably the datafile +i/o interface (DFIO), a general purpose binary file record manager to be used +in the new images structures project and elsewhere in IRAF. + +The main facilities provided by POE are for access to general header fields, +access to the variable length aspect and temporal records, event list i/o, +and event filtering. The POE header is a type of record, and the aspect and +temporal "records" are arrays of records (tables), as is the event list itself. +Any record access problem involving large records commonly involves the +related problems of indexing and selection based on a user supplied predicate +(boolean expression or filter in our case). + +In the general case, we would like the POE interface to be able to support +data from a variety of detectors or instruments, not only those used in high +energy astrophysics but those used in optical and radio observatories as well, +hence the details of the data structures must be allowed to vary without +affecting the interface itself. Ideally the POE files should be maintained +in a machine independent format, and the applications programs using POE +should not be affected by changes to the data format, and indeed should be +usable directly with any of several similar data formats, or with data formats +that evolve over time. + +All these observations lead us to conclude that a general implementation of +the POE interface has much in common with the general record access problem, +hence the association of POE with DFIO. Quick-POE will provide much the same +functionality at the applications level but will be less general, i.e., the +binary record structures as seen by applications will be mapped directly onto +external storage (the main contribution of DFIO is data independence and +flexibility). This means that initially the applications will be tied to +a specific format datafile, changes to applications structures will require +reformatting the data, and the datafiles will probably be machine dependent. + +This approach would be unacceptable in the long run, as the need arises to +support a variety of instruments, but is viable for initial applications +development provided a DFIO based implementation eventually replaces the +initial interface. There should be little difference in terms of functionality +and efficiency between QPOE and POE; in fact QPOE may have the edge over POE +in terms of efficiency, since it will be less general. It is likely that +applications developed for QPOE will be usable with POE with few if any +changes, e.g., by reimplementing QPOE as a layer on top of the more general +POE interface. The main motivation for implementing a DFIO based POE will be +to provide increased data independence, a machine independent binary data +format, and the ability to support a variety of instruments with a single +interface. + +Although some throw away code will have to be written to implement the QPOE +interface, most of the complexity of the interface lies in the event filtering +code, which should be reusable in the final interface. Low level, custom +(non-DFIO) selection code is required for POE due to the unusual requirements +for region and temporal filtering, and the potentially extremely high data +volume (>10**6 event records). This implies that DFIO itself will have to be +a layered interface, supporting low level access to the packed data records +for applications with unusual efficiency requirements (it will be). Finally, +it is already evident that the low level file manager required by QPOE has +much in common with the access method code planned for DFIO, hence can serve +as a prototype for the DFIO file manager. + +The remainder of this document will deal only with the details of the +QPOE interface. The DFIO interface has already been specified, +including a sketch of a data definition for a POE file. See \fIThe IRAF +Datafile I/O Interface\fR, February 1988. The region filtering +code in POE will make use of the Pixel List I/O (PLIO) interface, described +in \fIThe IRAF Pixel List Package\fR, February 1988. The latter interface +has already been implemented, as have all other interfaces (e.g., SYMTAB) +required by QPOE. + +.NH 2 +Architecture + + The architecture of IRAF as it pertains to the QPOE (and POE) interface +is summarized in the figure below. + +.ks +.nf + IMIO + IKI + IK-POE + POE + PLIO + [DFIO] + SYMTAB + FIO +.fi +.ke + +As indicated in the figure, QPOE depends most heavily on the VOS interfaces +PLIO (pixel list i/o), used for spatial filtering, SYMTAB (the general symbol +table package), used to manage the file header and aspect and temporal records, +and FIO (file i/o), used to access the binary file in +which the QPOE data is stored (low level, unbuffered asynchronous i/o is +used by the QPOE file manager). The QPOE interface is accessed both directly +by applications code, and by the IMIO interface via an image kernel, shown +as IK-POE in the figure. IK-POE and QPOE comprise the code to be written to +implement the QPOE interface. + +The QPOE interface consists of the POE file itself, a binary data structure +[largely] private to the QPOE interface, and a set of procedures for creating, +writing into, and reading from POE files. The procedures fall into several +categories, i.e., +.ls 4 +.ls o +General QPOE file management procedures. These include routines for creating, +deleting, renaming, opening, and closing POE files, plus set/stat routines +for setting and querying the file parameters and interface options. +.le +.ls o +General header access procedures. These include a conventional set of keyword +driven typed scalar get/put routines, plus get/put routines for accessing +variable length typed and opaque binary arrays (e.g., history records and the +aspect and temporal records). +.le +.ls o +Event i/o procedures. These are routines for initially preparing and +subsequently reading (sequentially with seek) the main event list, e.g., +the raw "get next photon" routine. +.le +.ls o +The selection subpackage. Included are routines for opening and incrementally +compiling a user supplied selection predicate (filter) input as a formatted +text string, and for testing individual event records to see whether they +satisfy the given expression. +.le +.le + +All binary data structures other than simple scalar variables, e.g., the +aspect and temporal records and the event structure, are described in QPOE +by compile time bound SPP binary data structure definitions, provided in a +standard interface include file (\fI<qpset.h>\fR) referenced by both QPOE and +selected applications. When the interface is layered upon DFIO these +structures could continue to be used, since DFIO will have the ability to +define runtime mappings of conventional application defined structures onto +the physical data datafile (POE file) structures. + +.NH 2 +Interface Specification + + The quick-POE interface (QPOE, package prefix `qp') is a set of procedures +for accessing \fIpoefiles\fR, or position ordered event files. Each poefile +consists of a \fBheader\fR of arbitrary size and content containing zero or +more named scalar or variable length (opaque, typeless) fields, plus an +\fBevent list\fR consisting of zero or more event structures. +The event structure is fixed at compile time via a conventional SPP structure +declaration in the include file \fB<qpset.h>\fR. + +.NH 3 +Interface Procedures + + The QPOE procedures fall into three main categories, the primary user +interface procedures (general datafile management, header access, and filtered +event i/o), the low level or raw event i/o procedures, and the low level +selection expression compile and evaluate procedures. + +.NH 4 +Header Access Procedures + + The routines described in this section are used to create, open, or +otherwise manipulate poefiles, to define new header parameters or query the +existing parameter set, and to read and write the values of both scalar and +vector parameters of various standard and poefile-specific datatypes. +These operators are summarized in the figure below. + +The function of most of these procedures should be obvious. +The \fIqp_access\fR, \fIqp_delete\fR, \fIqp_rename\fR, and \fIqp_copy\fR +operators perform the implied operation on the named poefile. +The poefile may be rebuilt with \fIqp_rebuild\fR, recovering any unused +space and rendering storage for the internal data structures (logically) +contiguous in the process (a rebuild is just a copy/rename/delete). + +The \fIqp_open\fR procedure must be called to open or create a poefile, +before it can be accessed. The NEW_FILE and NEW_COPY modes are supported +for creating new files. If NEW_COPY mode is specified, a reference file +may be specified (via the descriptor \fIo_qp\fR) from which the new file +is to inherit the header but no data (no event list). +The \fIqp_seti\fR and \fIqp_stati\fR procedures are used to set and stat +any parameters affecting QPOE i/o, and \fIqp_sync\fR updates an opened +poefile on disk. + +The \fIqp_get\fR and \fIqp_put\fR scalar functions behave as for the other +VOS interfaces, e.g., they will abort if the named parameter does not exist, +or if the implied datatype conversion is illegal. The \fIqp_add\fR +procedures are equivalent to the \fIqp_put\fR procedures except that they +will create the named parameter if it does not already exist (see also +\fIqp_addf\fR, discussed below). + +.nf + yes|no = qp_access (poefile, mode) + qp_copy (poefile, newfile) + qp_rename (poefile, newfile) + qp_rebuild (poefile) + qp_delete (poefile) + + qp = qp_open (poefile, mode, o_qp) + qp_seti (qp, param, ival) + ival = qp_stati (qp, param) + qp_sync (qp) + qp_close (qp) + + val = qp_get[bcsilrdx] (qp, param) + qp_gstr (qp, param, outstr, maxch) + qp_put[bcsilrdx] (qp, param, val) + qp_pstr (qp, param, strval) + qp_add[bcsilrdx] (qp, param, defval, comment) + qp_astr (qp, param, strval, comment) + + fd = qp_popen (qp, param, mode, type) + nelem = qp_read (qp, param, buf, nelem, first, dtype) + qp_write (qp, param, buf, nelem, first, dtype) + + yes|no = qp_accessf (qp, param) + qp_deletef (qp, param) + qp_renamef (qp, param, newname) + qp_addf (qp, param, dtype, maxelem, comment, flags) + nelem = qp_queryf (qp, param, dtype, maxelem, comment, flags) + + list = qp_ofnl[su] (qp, template) + nch|EOF = qp_gnfn (list, outstr, maxch) + qp_cfnl (list) +.fi + +Array valued parameters may be randomly read with \fIqp_read\fR and +written with \fIqp_write\fR; arrays may be any length, and will be +automatically extended in a write. The only way to shorten an array +parameter is to copy it and delete the old parameter. The typed read and +write functions allow automatic type conversions, and external storage of +the data in a machine independent form (should the interface choose to do so). +In addition to the standard SPP types, QPOE supports the special types +TY_EVENT, TY_ASPECT, and TY_TEMPORAL. Finally, the type TY_OPAQUE denotes +an array of element size SZ_CHAR, which will be copied to and from external +storage without the data being modified in any way (note that opaque data +is machine independent only if the application encodes it that way). + +Alternatively, an array valued parameter may be opened as a random access +\fIfile\fR with \fIqp_popen\fR, and then read or written with conventional +FIO calls. The value of the \fItype\fR parameter must be TEXT_FILE or +BINARY_FILE, as for a conventional file. If the type is TEXT_FILE then +only text data may be stored in the file, and text data will be byte packed +on disk. The BINARY_FILE type is equivalent to the QPOE type TY_OPAQUE. +File i/o to a QPOE parameter is equivalent to file i/o to a conventional +binary file in terms of both efficiency and semantics, i.e., the data is not +modified in any way, and the "files" may be any size (the main semantic +difference is that deleting the parameter does not immediately free the space). +A parameter opened as a file with \fIqp_popen\fR is closed with the FIO +\fIclose\fR routine. + +Although new parameters may be defined when first written to by calling one of +the typed \fIqp_add\fR functions, the most general procedure for adding new +parameters is \fIqp_addf\fR, which allows the datatype and vector length of +the parameter to be explicitly specified, along with a comment describing the +new parameter. The procedure \fIqp_accessf\fR tests if the named parameter +exists, and \fIqp_deletef\fR and \fIqp_renamef\fR make it possible to delete +and rename parameters, e.g., for implementing array copy procedures. +The \fIqp_queryf\fR procedure returns the datatype, allocated vector length, +current vector length, and comment field of the named parameter. + +The field name list procedures (\fIqp_ofnl[su]\fR etc.) are used to obtain +the names of all header parameters matching the given template; a null or "*" +template returns the names of all header parameters. This is the only way by +which an application without apriori knowledge of the field names can determine +what is in the header, e.g., to list or copy the header. + +.NH 4 +Filtered Event I/O Procedures + + The \fBevent i/o\fR subpacke provides sequential i/o facilities for the +main event list of the poefile. These procedures, known as the QPIO (QPOE +event i/o) package, provide read or write (append) access to the event list, +optionally filtered when reading to select events spatially or by event +attribute. + +Under QPOE, an event list is stored as a variable length array (i.e., as a +named header parameter) of type \fIevent\fR. The QPIO package takes this +basic object and adds additional structure for more efficient i/o, e.g., events +are blocked into large, fixed size \fIbuckets\fR of N events, the first two +events of each bucket containing the minimum and maximum event values for that +bucket. If the event list is sorted an \fIindex\fR may be maintained for the +list; this index, plus the min/max event values maintained for a bucket, are +used to optimize basic event i/o and filtering. During event i/o the raw +event list may be filtered spatially or by event attribute. All this is +transparent to the application, which merely opens the event list parameter +and begins reading (or writing) blocks of events. + +Before i/o can take place the named event list parameter is opened with +\fIqpio_open\fR. The selection filter to be used by QPIO may be specified +via a selection expression passed in by the user at poefile open time (as +part of the poefile name), at QPIO open time (as part of the parameter name), +or in subsequent calls to \fIqp_addfilter\fR (each call incrementally modifies +the current filter) or to \fIqp_setfilter\fR (each call replaces the affected +portion of the current filter). A region mask may also be specified with +\fIqp_setmask\fR; if no mask is specified, the default rejection mask is used +(or more precisely, its inverse). + +.nf + qpio = qpio_open (qp, param, mode) + qpio_mkindex (qpio, key, nelem) + qpio_setrange (qpio, vs, ve, ndim) + qpio_[add|set]filter (qpio, selexpr) + nchars = qpio_getfilter (qpio, outstr, maxch) + qpio_setmask (qpio, pl) + pl = qpio_getmask (qpio) + nev|EOF = qpio_getevents (qpio, ev, maskval, maxevents) + qpio_putevents (qpio, ev, nevents) + qpio_readpix (qpio, obuf, vs, ve, dxim, xblock, yblock) + qpio_close (qpio) +.fi + +Events are read sequentially with \fIqp_getevents\fR, which fills in the +pointer array \fIev[maxevents]\fR with one or more pointers to event structs, +returning the number of events read as the function value, or EOF when the +event list is exhausted. Events are returned in the order in which they are +stored in the main event list. If a region mask is used for spatial +filtering, the mask value associated with the output events is returned in +\fImaskval\fR. Filtering and subranges are supported only for reading; +\fIqp_putevents\fR may only be used to append to the output poefile. +At present, poefiles are not randomly updatable, as this would require +runtime editing of the compressed event lists and it is not clear how useful +such a feature would be. + +If less then the entire image is to be accessed then \fIqp_setrange\fR +may be called to specify the region of the poe image from which events are +to be read (the vector coordinates \fIvs\fR and \fIve\fR are specified +relative to the predefined primary event coordinate system, i.e., the PO +coordinates). Repeated calls to \fIqp_setrange\fR may be made to access +multiple regions of the image, or to rewind the i/o pointer for a region. + +An alternative to event i/o is provided by \fIqp_readpix\fR, which samples +the event list using the current filters and blocking factor, +generating \fInpix\fR pixels beginning with the pixel at the image coordinates +specified by the vector \fIv\fR. This is the routine used by the POE IMIO +image kernel to read from a poefile. Only integer pixels are supported. +On output, each pixel value is a count of the number of filtered events +mapping into that pixel. A region mask may be used to filter the event list, +but the ability to discriminate between different regions by the mask value +is lost. + +A poefile may contain any number of event list parameters, although most +files are expected to store only the main event list. +As an alternative to QPIO, event-array parameters may be accessed directly +via the normal header access parameters, e.g., \fIqp_read\fR, but i/o may be +somewhat less efficient (due to the copyout), the bucket structure will be +visible, and no filtering is possible. In short, the raw event list will +be accessed as an array, returning the min/max events in each bucket along +with the data events. + +In the current implementation, the event structure is a fixed, predefined +binary structure, and all event i/o is expressed in terms of pointers to event +structures. The event structure is defined in the include file +\fB<qpset.h>\fR, discussed in the appendix. + +.NH 4 +Selection Subsystem Procedures + + The \fBselection subsystem\fR is a facility used to perform runtime +filtering of the event list, returning to the calling program only those +events satisfying some user defined selection criteria. The selection +subsystem is driven by a selection expression provided by the user as a +formatted string, normally at image (poefile) open time. The selection +expression syntax itself is independent of the procedural interface and is +described separately in section 2.3.2. The selection procedures are +summarized in the figure below. + +.nf + ex = qpex_open (qp, expr) + ok|err = qpex_modfilter (ex, exprlist) + nchars = qpex_getfilter (ex, outstr, maxch) + nev = qpex_evaluate (ex, i_ev, o_ev, nev) + qpex_close (ex) +.fi + +A selection expression, input as the string \fIexpr\fR, is compiled with +\fIqpex_open\fR, which returns a pointer to the runtime descriptor (filter) +for the given compiled selection expression. If \fIexpr\fR is the null +string the filter returned will pass all events. + +An active filter may be modified with \fIqpex_modfilter\fR, which combines +the expression \fIexpr\fR into the current filter, allowing complex +filters to be built up in several calls, or allowing an application to modify +a base filter without knowledge of the base filter. The expression consists +of a list of comma delimited "attribute = exprlist" terms. If the assignment +is specified as "=" the term for the given attribute is replaced; if the +assignment is "+=" the term for the given attribute is further qualified. +A text representation for the current filter may be obtained at any time with +\fIqpex_getfilter\fR. The boolean function \fIqpex_evaluate\fR is called to +test whether a specific event meets the selection criteria, i.e., to test +whether or not \fIexpr\fR is true for the given event. + +Selection is normally performed transparently to the application by the QPIO +interface, which calls the routines described in this section to create and +apply event-attribute filters. + +.NH 3 +Spatial and Event Attribute Filtering +.NH 4 +Selection Syntax + + Selection expressions are used to construct \fIfilters\fR to specify the +rejection mask and region of interest filters before reading the event list +via QPIO. Due to the complexity of event attribute selection, selection +predicates are specified syntactically, i.e., as an expression input as a +text string. These filters may be input by the user as part of the image +or poefile specification (object name), transparently to applications code, +or they may be constructed by the application via calls to the QPIO or +selection routines. Complex or frequently referenced filters may be stored +in text files and referenced by filename if desired. It does not matter +whether a filter is input all at once, or compiled incrementally. + +An event attribute filter consists of a set of filters for each event +attribute. By default, i.e., if no filter is specified, all attribute values +are passed. If a filter is specified for an attribute, the filter specifies +either a bitwise mask value, or a list of acceptable values or ranges of values +for the attribute. An event is passed if and only if all event attribute +filters pass the event. If a list of acceptable values are specified, the +list may be any length, with little impact on filtering efficiency. + +The basic event attribute filter syntax consists of a list of attribute +filters, e.g.: + + attribute = values [, attribute = values ...] + +where \fIattribute\fR is the attribute name, e.g., a position attribute, +time, energy, and so on, and \fIvalues\fR is a mask or list of values. +Mask values are integers prefixed by `%', e.g., + + attribute = %1003B + +Note that the mask may be specified in decimal (the default), octal (`b' or +`B' suffix), or hex (`x' or `X' suffix), in accord with the usual IRAF +conventions. The meta-characters used in selection expressions have been +selected to avoid or at least minimize the need to quote such expressions +in CL commands. + +A specific value or list of values may be specified as a +simple integer constant, or comma delimited list of constants, e.g.: + +.nf + attribute = 3 +or + attribute = 3, 5, 20X +.fi + +Ranges are specified using the `:' notation, e.g., + + attribute = 3, 5, 8:11 + +A `!' may be prepended to indicate the opposite, i.e., "everything but": + +.nf + attribute = !%14B +or + attribute = 3, !1:10 +.fi + +Open ended ranges may be used to indicate that the range includes all values +less than or equal to or greater than or equal to the given value, e.g., + + attribute = :100 + +denotes all values less than or equal to 100. + +File inclusion or \fImacro expansion\fR is denoted by a C-like function call +notation, e.g., + +.nf + macro() +or + macro(a,b) +.fi + +Any arguments are expanded via string substitution when the text of the macro +or include file is expanded. Include files should have the extension ".qpm". +Macros are permitted only if the variable \fBqpinit\fR is defined in the user's +environment, the string value consisting if the filename of the user's QPOE +macro file. In a reference to a macro \fImacro\fR, QPOE will look first in +the macro definitions file pointed to by \fIqpinit\fR for the named macro, +then it will look for the file "\fImacro\fR.qpm" in the current directory. + +Parenthesis are optional and may be included to, for example, make attribute +value lists more easily identifiable. If a line ends in a comma or backslash +continuation is assumed; blank lines and comment lines are ignored. +The syntax for attributes with floating point values is identical to that +for integers except that mask values are not allowed. + +.NH 4 +Region Specification + + QPOE does not itself contain any syntax-level support for specifying +region masks, e.g., via a list of include and exclude circles and other +shapes. The reason for this is that it is too difficult to come up with +a sufficiently general scheme at the level of an interface like QPOE; +there are too many ways to specify regions, hence in general such region +specification must be done at the applications level. QPOE does however +include very general and efficient support for region analysis provided the +region mask is input already encoded into a PLIO binary mask. Since PLIO +includes high level primitives for defining masks in terms of include and +exclude circles, boxes, lines, polygons, etc., it is easy to extend QPOE at +the applications level to include support for a region specification language +tailored to the specific application. + +While QPOE cannot itself process a user defined region description to create +new region masks, it is possible to \fIselect\fR from any number of region +masks if these are prepared in advance using other systems facilities or +applications programs. PLIO region masks may be stored in the QPOE header +as named parameters of type opaque binary array, or they may be stored in +external binary files. The region mask to be used may be specified by +including an assignment of the form + + mask=[\fIparam\fR|\fIfile\fR.pl] + +in the selection expression. Unless otherwise specified, this region mask +will be combined with the default rejection mask for the poefile (the final +mask will be the region mask \fIand\fR-ed with the \fInot\fR of the rejection +mask). + +.NH 4 +Predefined Selection Keywords + + While the syntax of a selection expression is an inherent part of the +QPOE interface, the names of the event attributes used in selection +expressions are logically part of the event structure, and ideally should +be stored with the data and used by the interface only to determine the +attribute datatypes and offsets into the event structure when the selection +expression is compiled at runtime. We should not really be documenting the +specifics of the POE external data structures here, but in QPOE these data +structures are wired into the interface, so it is appropriate to do so. + +The following \fIstandard event attributes\fR are defined. Minimum match +abbreviations are of course permitted. The keyword \fIpi\fR is an acceptable +alias for \fIenergy\fR (\fIpi\fR and \fIpha\fR are examples of discipline +dependent terminology which should be associated with the data and not the +interface). + +.nf + X short range in X (PO coords) + Y short range in Y (PO coords) + TIME real time event was recorded + ENERGY,PI int energy of event + PHA int pulse height +.fi + +The event attributes may also be referred to using the generic notation +[\fIsir\fR]\fIN\fR, which refers to each attribute by its datatype and +struct offset (byte units, zero indexed) rather than by name. For example, +if the event attributes shown above are assumed to be shown in the order +in which the fields are stored in the event struct, the \fItime\fR field +could also be referred to as \fIR4\fR, and \fIpha\fR as \fII10\fR. This +crude but effective technique may be used to reference any private +(nonstandard) fields of the event struct in selection expressions. + +The following additional, non-event keywords are defined: + +.nf + BLOCK int \fIqp_readpix\fR blocking factor + MASK string region mask to be used + FILTER string region filter to be used + REJMASK string rejection mask to be used + REJFILTER string rejection filter to be used +.fi + +The default values for these parameters are taken from datafile header +parameters of the same name, if such are found. Masks are specified either +by the name of a header parameter of type opaque binary array (containing +an encoded PLIO mask), or by the name of a PLIO mask file, extension ".pl". +Named filters are specified by the name of a header parameter of type char +array, or by the name of a text file (extension ".qpf"), where in either +case the named object contains the selection expression text. + +.NH 3 +Interface Set/Stat Parameters + + The internal parameters for the QPOE interface, and all user accessible +data structures, e.g., the event and other structures, are defined in the +global system include file \fB<qpset.h>\fR. This file should be referred to +for up to date documentation on these definitions and structures; the +discussion which follows may not be kept up to date. + +The following interface parameters may be accessed via the \fIqp_seti\fR and +\fIqp_stati\fR procedures: + +.nf + QP_XRESOLUTION resolution of an event x-coordinate + QP_YRESOLUTION resolution of an event y-coordinate + QP_LENEVENT length of an event structure + QP_LENINDEX resolution of the event list index + QP_BUCKETSIZE event list bucket size, nevents + QP_PAGESIZE datafile page size, bytes + QP_CACHESIZE number of buffers in data buffer cache + QP_MAXFILES max lfiles in datafile (fixed) + QP_NFILES query number of lfiles in datafile + QP_NPAGES query number of pages in datafile + QP_FREEPAGES query number of free pages in datafile +.fi + +The parameters shown above may be set only at datafile creation time. +The X and Y resolution parameters define the range of event x,y coordinates. +The resolution of the event list index is set by QP_LENINDEX, and may be less +than the full resolution of the event pixel Y-coordinate. +The remaining parameters control how storage is physically allocated in the +datafile and in any event lists. + +.NH 2 +Detailed Design +.NH 3 +Event Attribute Filtering + + The point of event-attribute filtering is to test an event to see if it +satisfies a user defined event selection expression. A selection expression +may be decomposed into a list of simple, independent expressions for the +individual event attributes; the event satisfies the full expression only +if the value of each event attribute satisfies the associated attribute +expression. Currently, attribute expressions are limited to bitmasks, lists +of acceptable values, or lists of ranges (inclusive) of acceptable values. + + attr1=expr, attr2=expr, ... + +The highly constrained nature of event-attribute expressions makes expression +evaluation straightforward and fast. Expression evaluation is implemented +by logically negating each attribute expression and testing each in turn; +expression evaluation ends either when an attribute test fails, in which case +the event is rejected, or when the end of the attribute expression list is +reached, in which case the event is passed. + +The obvious way to implement such an expression evaluator is with a simple +interpreter. The expression is parsed and compiled to produce a simple +interpreter program, using the instructions shown in the figure below. + +.ks +.nf + \fIinstruction arguments\fR + + MSK[sir] offset maskval bitwise mask test + EQL[sir] offset value equality test + LEQ[sir] offset value less than or equal + GEQ[sir] offset value greater than or equal + RNG[sir] offset lowval highval range test (inclusive) + LUT[sir] offset lut lookup table + NOT invert test + RET return, pass event +.fi +.ke + +An interpreter program consists of a series of these instructions. The +\fIoffset\fR argument gives the offset of the event attribute (field) to +be tested; the datatype of this field must match that of the instruction +and of the data argument or arguments, if any. Only event attributes for +which restricted values were specified are tested, hence the cost of the +evaluator depends only upon the complexity of the expression to be evaluated. +An interpreter of this type can be coded very efficiently as a switch-case +statement (jump table) within an optimized DO-loop. + +Simple attribute value tests are most efficiently coded as several \fIMSK\fR, +\fIEQL\fR, etc., instructions. The only case of any complexity is where the +attribute has a long list of acceptable values or ranges of values. This is +most efficiently coded using a lookup table, using the \fILUT\fR instruction +shown in the figure. A lookup table test may be preceded by a range test to +limit the size of the lookup table required. For an integer or short integer +attribute, the lookup table will be a \fIboolean\fR table containing one entry +for each possible value of the attribute in the range spanned by the table. + +Use of a lookup table for floating point attributes is more difficult since +an enormous lookup table might be required to preserve the resolution of +the floating point numbers used to define ranges. The solution is to employ +an \fIinteger\fR (rather than boolean) lookup table of \fIreduced resolution\fR. +The floating point value of the attribute to be tested is mapped into a bin of +the lookup table. The integer value of the table entry has one of the +following values: + +.ks +.nf + 0 Reject all FP numbers mapping to this bin. + 1 Accept all FP numbers mapping to this bin. + + N Some of the FP numbers mapping to this bin are + legal, and some are not. The value N is the + address of a segment of interpreter code to be + executed to test a FP value mapped to this bin. +.fi +.ke + +The performance of this algorithm for floating point table lookup depends +upon the frequency with which 0 or 1 is encountered as the table value during +lookup; if 0 or 1 is encountered most of the time, then a floating point LUT +test is comparable in expense to an integer LUT test. But since we already +have to map a floating point number into an integer space of reduced +resolution, we can easily vary the resolution of the lookup table, +increasing the resolution of the table until the desired level of efficiency +is reached (the interpreter execution time for case N is pretty fast in any +case, so this is not critical). + +In summary, the expense of event-attribute filtering is directly proportional +to the number of attribute tests to be performed. An arbitrary number of +values or ranges of values may be specified for an attribute with little if +any affect on performance, even for floating point attributes (e.g., for +time-tagged quality filtering). + +.NH 3 +Region Filtering + + Region filtering is implemented in QPOE by the PLIO interface, which is +documented elsewhere. PLIO permits regions of arbitrary complexity to be +described and used for event filtering, with little overhead beyond that +already present for i/o on a large event list with no region filtering. +This assumes only that the event list is position ordered, and that the +region mask is specified in the PO (position ordered) coordinate system. +This makes it possible for QPIO to use the mask to reduce the number of +events to be examined; event attribute filtering is performed only on those +events read through the region mask (this is similar to masked image i/o, +i.e., the MIO package). + +If the event structure supports multiple coordinate systems and the region +mask refers to a non-PO coordinate system, then the only approach is to first +perform event-attribute filtering on the non-positional event attributes, +then for each event passing the event-attribute filter, fetch the mask value +corresponding to the x,y coordinates of the event. This is still an efficient +technique since only mask pixel lookup is required (no complicated region +list traversal is involved), but it will be significantly less efficient than +PO region filtering since we cannot take advantage of position ordering to +reduce the number of events to be examined, and the overhead of accessing +the region mask will be greater. + +.NH 3 +Datafile Layout and Access +.NH 4 +File Structure + + The QPOE file structure is private to the QPOE interface and is discussed +here only for the purpose of detailing (and documenting) the design of the +interface. The QPOE file is a random access, dynamically extendable, binary +file. Under QPOE these files will be partially, but not completely, machine +independent, hence file sharing by machines of different architectures will +not be provided initially. This will be rectified when management of the +datafile is later turned over to DFIO. + +To provide a reasonable degree of flexibility, QPOE contains many +variable length data structures, e.g., there may be any number of header +parameters, including array valued parameters of arbitrary size. New header +parameters may be added at any time, and new data may be appended to array +parameters at any time. This flexibility places certain demands upon the +low level file manager used to maintain these data structures in the datafile. + +All access to the physical datafile is via a low level binary +\fIfile manager\fR. The purpose of the file manager is to implement a +restricted implementation of the binary file abstraction upon a single host +level binary file. This provides the "lightweight" binary file mechanism we +need for QPOE. Since the file manager is a low level facility, it is +implemented using only the low level asynchronous i/o facilities provided +by FIO to read and write file pages, once the file has been opened. + +The file manager provides routines for creating new datafiles, and for +creating, deleting, etc., \fIlightweight files\fR (lfiles) within a datafile. +Storage for lfiles is allocated in units of datafile \fIpages\fR. For each +data page in the datafile there is an entry in the datafile \fIpage table\fR. +The page table itself is stored as an lfile (\fIlfile zero\fR) in the data +pages. Files at the file manager level are known only by their file number; +association of these file numbers with file names is left up to the higher +level code, and in the case of QPOE is done with the symbol table (which is +also stored as an lfile). + +.ks +.nf + +-----------------+ + datafile header fixed size + +-----------------+ + file table fixed size + +-----------------+ + | + data pages data and page table pages + | (arbitrarily large) + v +.fi +.ke + +The page table is a vector mapping datafile pages by file offset onto lfile +file numbers; the value of each page table entry is the file number of the +lfile to which the page is assigned. When the file manager opens an lfile +it scans the page table, extracting the page numbers of the pages assigned +to the lfile, to form a vector mapping lfile page offsets directly onto +datafile page offsets. New pages are always allocated at the end of the +datafile, and new lfiles are always allocated at the end of the file table, +hence lfile deletion will leave "holes" (unused storage) in both the datafile +pages and file table. A \fIrebuild\fR operation is required to reclaim the +space occupied by these holes. Deleted files are recoverable by merely +revalidating their file table entries. + +Every variable size object managed by QPOE is stored in the datafile as a +distinct lfile. Since storage for lfiles is allocated in units of file pages, +the minimum amount of storage used by a variable length object is 0 or 1 page. +Examples of variable size objects are the SYMTAB symbol table +used to describe the contents of the datafile header (and any other symbols +used by QPOE), the static data storage area (used to store the values of +scalar and static array valued header parameters), and individual variable +length arrays. Note that each variable length array is stored in the datafile +as a separate lfile; if the maximum size of an array is less then the page +size, it will be more efficient to store it as a static array. + +The most important example of a variable length array is the main event list +of the poefile. To improve i/o efficiency and speed selection, the event +structs stored in an event list are grouped together into \fIbuckets\fR, +as discussed earlier in section 2.3.1.2. Each bucket will always occupy an +integral number of file pages. Storage for buckets is allocated contiguously +in the datafile, and buckets are always read and written to disk in a single +i/o transfer. + +The most important physical datafile parameters are hence the page size and +the bucket size. A larger page size can improve i/o efficiency and reduce the +size of the page table, but can lead to significant wasted space if there are +many variable length arrays. Since the i/o system will move entire large +blocks of pages to and from disk whenever possible, use of a small page is +normally preferred. A large bucket size improves i/o efficiency for event +lists, but if the bucket size is too large then bucket searching takes longer, +and selection efficiency may decrease. + +.NH 4 +File Manager + + The function of the file manager is to map a set of lfiles onto a single +random access host binary file. The file manager must keep track of the size +and type of each file, and whether or not it has been deleted. +In addition, the file manager must maintain a page table for the entire +datafile, noting the lfile to which each page is assigned. While an lfile +is open the file manager must maintain the page vector for that lfile so that +lfile offsets may be mapped directly onto datafile offsets. + +The number of lfiles is fixed at datafile creation time, and lfiles are +referred to by file number. File number zero is the datafile page table; +the first user lfile is number one. A datafile with a max file count of one +would actually contain two lfiles, counting the page table. + +The file manager interface is summarized in the figure below. A new datafile +may be created or an existing datafile opened with \fIfm_open\fR. +If a new datafile is being created the page size and max file count may be +changed from their default values with calls to \fIfm_seti\fR, and the values +of these and other parameters may be queried at any time with \fIfm_stati\fR. +An opened datafile may be copied with \fIfm_copyo\fR, omitting deleted lfiles +and rendering file segments contiguous. The page size and max file count +may be changed in a copy operation if desired. + +The \fIfm_access\fR, \fIfm_rename\fR, and \fIfm_delete\fR routines perform +the indicated operation upon the named datafile. The \fIfm_rebuild\fR +routine rebuilds a datafile, discarding deleted structures and coalescing +storage for objects. This routine, as well as \fIfm_copy\fR, are built upon +on the lower level routine \fIfm_copyo\fR, which does the real work, and +which allows the structural attributes of the new datafile to be specified +in \fIfm_seti\fR cals. + +All i/o to lfiles is via the six routines beginning with \fIfm_lfopen\fR in +the figure below. These routines constitute a FIO binary file driver for +lfiles, and may be called directly, or passed to the FIO routine \fIfopnbf\fR +to open an lfile as a binary file (\fIfm_lfname\fR should be called first +to construct a pseudo-filename for the lfile so that \fIfm_lfopen\fR can +reconstruct the file manager descriptor, lfile number, and lfile type). +Note that the lfile driver routines are unbuffered and (potentially) +asynchronous, and that i/o must be in units of datafile pages. +(See the buffer cache routines described in the next section for a higher +level facility for i/o to lfiles). + +.nf + yes|no = fm_access (datafile, mode) + fm_rename (datafile, newname) + fm_copy (datafile, newname) + fm_delete (datafile) + fm_rebuild (datafile) + + fm = fm_open (datafile, mode) + fm_seti (fm, param, ival) + ival = fm_stati (fm, param) + fm_debug (fm, out, what) + fm_copyo (fm, fm_to) + fm_sync (fm) + fm_close (fm) + + lfile = fm_nextlfile (fm) + fm_lfname (fm, lfile, type, lfname, maxch) + + fm_lfopen (lfname, mode, lf) + fm_lfstati (lf, param, ival) + fm_lfaread (lf, buf, nbytes, offset, status) + fm_lfawrite (lf, buf, nbytes, offset, status) + fm_lfawait (lf, status) + fm_lfclose (lf, status) + + fm_lfstat (fm, lfile, statbuf) + fm_lfdelete (fm, lfile) + fm_lfundelete (fm, lfile) +.fi + +In a sense, all lfiles exist as zero length files when the datafile is +created, since the lfile descriptors are preallocated and the files are +known only by number. Lfiles become interesting when they are opened as +files with \fIfm_lfopen\fR, and data is written into the file. An lfile +may be deleted with \fIfm_lfstat\fR. All this does is set the delete bit +in the lfile descriptor, hence a deleted lfile may later be undeleted with +\fIfm_lfundelete\fR. The data in a deleted lfile is not lost until the lfile +is again opened and written into, or the datafile is rebuilt. Information +on a specific lfile (size, type, etc.) may be obtained with \fIfm_lfstat\fR. + +There is nothing about the file manager which is specific to QPOE, so it is +implemented as a separate, standalone facility, and may be used in applications +other than QPOE. + +.NH 4 +Buffer Cache + + For reasons of efficiency, QPOE maintains portions of the datafile in +memory buffers while a datafile is open. The main QPOE descriptor, symbol +table, and file manager descriptor and page table are maintained in special +runtime data structures internal to the respective interfaces. All other +data is stored in lfiles and accessed only upon demand. In particular, +storage for all static (non variable length) QPOE header parameters is +maintained in a single lfile, and storage for each variable length parameter +is allocated in a separate lfile. + +Since most access to QPOE header parameters is via simple gets and puts to +named parameters, lfile access is handled by QPOE transparently to the client +applications program. To avoid excessive disk i/o when randomly accessing +the datafile, it is desirable for QPOE to maintain a cache of several lfile +data buffers, e.g., so that accesses to a series of static parameters or +repeated accesses to read or write different parts of an array parameter +should incur minimal disk accesses. This buffer cache is implemented in +QPOE by simply opening each lfile as a file under FIO, leaving it up to FIO to +manage the file buffer, and maintaining a LRU cache of open lfiles in QPOE. +The number of buffers (open lfiles) is controlled by the QP_CACHESIZE +parameter. Since the lfile buffer cache is a general datafile related +facility, it is implemented by the file manager. + +.ks +.nf + fd = fm_getfd (fm, lfile, mode, type) + fm_retfd (fm, lfile) + fm_lockout (fm, lfile) + fm_debugfd (fm, out) +.fi +.ke + +The \fIfm_getfd\fR routine maps an lfile onto a file descriptor. A file +descriptor is opened on the lfile only when necessary. Once opened, an lfile +remains in the cache until forced out by the LRU replacement algorithm, +or the datafile is closed. Removal of an lfile from the cache (closing the +associated file descriptor) is permitted only after a call to \fIfm_retfd\fR; +calling this routine does not immediately close the file, it only permits it +to be closed. Most calls to \fIfm_getfd\fR should return a file descriptor +immediately, with very little overhead, with an already active file buffer, +hence repeated calls to the cache manager and FIO may be made without +incurring any disk accesses. + +Note that lfiles may be opened on file descriptors via direct calls to the +file manager, regardless of whether these lfiles are already open in the +buffer cache. This allows two or more independent file buffers to be +simultaneously active on the same lfile, but opens the possibility of loss +of data if the buffers overlap. If this is a problem, the routine +\fIfm_lockoutfd\fR may be called to prevent inadvertent use of an lfile by +the cache. This should be followed by a call to \fIfm_retfd\fR to clear the +lockout bit once the reason for the lockout (usually a noncached lfile open) +is gone. The routine \fIfm_debugfd\fR will print information on \fIout\fR +describing the contents of the buffer cache. + +.tp 24 +.NH 3 +Interface Structure +.NH 4 1 +Header Access Package (QP Routines) + + The structure of the general QPOE routines (mostly header access) is +illustrated in the figure below. + +.ks +.nf + +--------+ + | QP | + +--------+ + / \ + / \ + +--------+ +--------+ + | SYMTAB | | BCACHE | + +--------+ +--------+ + | + +--------------+ + | FILE MANAGER | + +--------------+ + + Figure 1. Structure of the Header Access Routines +.fi +.ke + +To fulfill a get or put header access, QPOE will access the symbol table +(SYMTAB) to lookup the symbol name and determine the symbol datatype, nelem, +lfile number, and lfile file offset where the value is stored. The buffer +cache (BCACHE) and FIO are then called to access the value of the parameter +in the datafile. + +.NH 4 +Filtered Event I/O Package (QPIO) + + The structure of the filtered event i/o package (QPIO) is illustrated in +the figure below. + +.ks +.nf + +--------+ + | QPIO | + +--------+ + / | \ + __________/ | \__________ + / | \ + +--------+ +--------+ +--------+ + | PLIO | | QPEX | | BCACHE | + +--------+ +--------+ +--------+ + | | + +--------+ +--------------+ + | SYMTAB | | FILE MANAGER | + +--------+ +--------------+ + + Figure 2. Structure of QPIO Routines +.fi +.ke + +In the typical \fIgetevents\fR call, QPIO will call PLIO to determine the +next region of the stored image (event list) to access, then if the event +data is not already in a data buffer, FIO is called to read the data (bucket), +using the event list index, an integer array valued parameter, to determine +what bucket to read. The events in the bucket are then examined and optionally +filtered via calls to QPEX, returning pointers to the passed events in an +output argument. This process terminates when either the mask value changes +or at least one event has been returned and a new bucket is required to +continue reading. diff --git a/sys/qpoe/README b/sys/qpoe/README new file mode 100644 index 00000000..dd48198b --- /dev/null +++ b/sys/qpoe/README @@ -0,0 +1,323 @@ +QPOE -- Prototype POE (Position Ordered Event file) Interface. +See QPOE.hlp for detailed information. +----------------------------------------------------------------- + + +1. QPOE (General QPOE file access) + + [ --- external routines --- ] + + qp_parse (expr, poefile, sz_poefile, paramex, sz_paramex) + yes|no = qp_access (poefile, mode) + qp_copy (o_poefile, n_poefile) + qp_rename (o_poefile, n_poefile) + qp_rebuild (poefile) + qp_delete (poefile) + + ptr = qp_open (poefile, mode, o_qp) + qp_set[ir] (qp, param, value) + val = qp_stat[ir] (qp, param) + qp_debug (qp, out, what) + qp_sync (qp) + qp_close (qp) + + qp_add[bcsilrdx] (qp, param, value, comment) + qp_astr (qp, param, value, comment) + val = qp_get[bcsilrdx] (qp, param) + nchars = qp_gstr (qp, param, outstr, maxch) + qp_put[bcsilrdx] (qp, param, value) + qp_pstr (qp, param, strval) + + n = qp_read (qp, param, buf, maxelem, first, datatype) + qp_write (qp, param, buf, nelem, first, datatype) + fd = qp_popen (qp, param, mode, type) + mw = qp_loadwcs (qp) + qp_savewcs (qp, mw) + + yes|no = qp_accessf (qp, param) + qp_deletef (qp, param) + qp_renamef (qp, param, newname) + qp_copyf (o_qp, o_param, n_qp, n_param) + qp_addf (qp, param, datatype, maxelem, comment, flags) + nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags) + nelem = qp_lenf (qp, param) + nchars = qp_expandtext (qp, s1, s2, maxch) + + ptr = qp_ofnl[su] (qp, template) + ptr = qp_ofnl (qp, template, sort) + n|EOF = qp_gnfn (fl, outstr, maxch) + len = qp_lenfnl (fl) + qp_seekfnl (fl, pos) + qp_cfnl (fl) + + [ --- internal routines --- ] + + qp_bind (qp) + dtype = qp_dtype (qp, datatype, dsym) +nchars = qp_elementsize (qp, datatype) + nchars = qp_sizeof (qp, dtype, dsym) + qp_mkfname (poefile, extn, fname, maxch) + ival = qp_ctoi (str, ip, ival) + dval = qp_ctod (str, ip, dval) + + ptr = qp_gmsym (qp, macro, textp) + ptr = qp_gpsym (qp, param) + nfields = qp_parsefl (qp, fieldlist, dd) + qp_inherit (n_qp, o_qp, out) + dtype = qp_getparam (qp, param, o_pp) + dtype = qp_putparam (qp, param, o_pp) + qp_flushpar (qp) + + gt = qp_opentext (qp, text) # token i/o + token = qp_nexttok (gt) + token = qp_gettok (gt, tokbuf, maxch) + token = qp_rawtok (gt, outstr, maxch) + nargs = qp_arglist (gt, argbuf, maxch) + qp_closetext (gt) + + ptr = qm_access () # macros + ch = qm_getc (fd, ch) + qm_scan (qm, fname, flags) + qm_scano (qm, fd, flags) + qm_[set|upd]defaults (qm, qp) + qm_setparam (qm, param, valstr) + ptr = qm_symtab (qm) + + +2. QPIO (Event I/O) + + [ --- external routines --- ] + + io = qpio_open (qp, paramex, mode) + qpio_close (io) + + qpio_setrange (io, vs, ve, ndim) + ndim = qpio_getrange (io, vs, ve, maxdim) + qpio_setfilter (io, expr) +nchars = qpio_getfilter (io, outstr, maxch) + qpio_set[ir] (io, param, value) + val = qpio_stat[ir] (io, param) + mw = qpio_loadwcs (io) + qpio_mkindex (io, key) + + qpio_putevents (io, i_ev, nevents) + n|EOF = qpio_getevents (io, o_ev, maskval, maxev, nev) + nev = qpio_readpix[si] (io, obuf, vs, ve, ndim, xblock, yblock) + + [ --- internal routines --- ] + + ok|err = qpio_parse (io, expr, filter, sz_filter, mask, sz_mask) + qpio_loadmask (io, mask, mergeflg) +bkno|EOF = qpio_rbucket (io, bkno) + qpio_wbucket (io, n_bkno) + qpio_sync (io) + + +3. QPEX (Event Attribute Filtering) + + [ --- external routines --- ] + + ex = qpex_open (qp, expr) +ok|err = qpex_modfilter (ex, exprlist) +nchars = qpex_getfilter (ex, outstr, maxch) + nc = qpex_getattribute (ex, attribute, outstr, maxch) + nr = qpex_attrl[ird] (ex, attribute, xs, xe, xlen) + nev = qpex_evaluate (ex, i_ev, o_ev, nev) + qpex_close (ex) + + [ --- internal routines --- ] + + v = qpex_parse[dir] (expr, xs, xe, xlen) + v = qpex_sublist[dir] (x1, x2, xs,xe,nranges,ip, o_xs,o_xe) + v = qpex_codegen[dir] (ex, atname, assignop, expr, offset, dtype) + qpex_delete (ex, offset, dtype) + + ptr = qpex_pbpos (ex) + offset = qpex_refd (ex, value) + ptr = qpex_dballoc (ex, nelem, dtype) + ptr = qpex_dbpstr (ex, strval) + qpex_pbpin (ex, opcode, arg1, arg2, arg3) + qpex_mark (ex, pb_save, db_save) + qpex_free (ex, pb_save, db_save) + + nr = qp_rlmerge[dir] (os,oe,olen, xs,xe,nx, ys,ye,ny) + + +4. INTERFACE SYNTAX + +Default parameter and domain names: + + "deffilt" # default event filter (all event lists) + "defmask" # default region mask (all event lists) + "deffilt.<evl>" # default event filter for event list <evl> + "defmask.<evl>" # default region mask for event list <evl> + "event" # default name of user event datatype + "events" # default event-list parameter + +QPIO expression syntax: + + [ evl-param ][ `[' [`!'] keyword [(`:='|`='|`+=') expr], ...`]' ] + +where <evl-param> defaults to "events" if not given, and where <keyword> may +be any of the following, or a term of an event attribute expression. + + block # blocking factor for image matrix + debug # debug level (integer, 0=nodebug) + filter # event attribute filter (expression) + key # event key (Y,X) fields (e.g.(s10,s8)) + noindex # don't use index even if present + param # name of event list header parameter + mask # region mask + rect # subregion of image, e.g, rect=[*,100:400] + +Any unrecognized keyword=expr terms are passed on to the event attribute +filter, hence the "filter=(expr)" syntax is optional. + +QPEX expression syntax (the <expr> in "filter=<expr>" above): + + '(' attribute=expr [, attribute=expr...] ')' + +where <attribute> is the "physical" name (type code plus byte offset) of +a field of the event structure, and expr is + + %N bitwise mask test + !%N negated bitwise mask test + +or some combination of + + N equality test + :N open range (less than or equal to N) + N: open range (greater than or equal to N) + M:N range (M to N inclusive) + expr,expr,... list of values or ranges + '(' expr ')' parenthesized expr + '!' expr + +MACRO syntax: + + macro replace <macro> by defined value + macro(arg,...) replace <macro> with argument substitution + @file replace <@file> by contents of file + `cmd` replace <`cmd`> by output of the CL command "cmd", + replacing all newlines by spaces + @file(arg,...) file pushback with argument substitution + `cmd`(arg,...) command output pushback with argument substitution + +Macro define syntax: + + define macro replacement-text + set parameter value + +where <macro> is any identifier, and <replacement-text> is literal text to +be pushed back into the input and rescanned when <macro> is encountered in +the input stream. <replacement-text> may contain symbols of the form '$N' +denoting places where argument substitution is to be performed during pushback. +The special builtin macro $DFN will be replaced by the datafile name, returned +as a string token. + +<parameter> denotes a QPOE interface parameter the value of which is to be +set, e.g., to set the size of a buffer to be created at run time. The +following interface parameters are defined: + + "bucketlen" QPIO bucket length, nevents + "cachesize" number of file descriptors in lfile cache + "indexlen" number of hash entries in symbol table index + "maxlfiles" maximum number of lfiles in datafile + "pagesize" page size, bytes, of datafile + "sbufsize" initial symbol table size, su + "stablen" initial symbol table string buffer size, chars + "progbuflen" QPEX program buffer size (compiled instructions) + "databuflen" QPEX data buffer size (initialized data space) + "nodeffilt" disable the use of any default event filters + "nodefmask" disable the use of any default region masks + "maxpushback" max characters pushback (for macro expansion) + "maxfrlutlen" max full resolution lookup table length + "maxrrlutlen" max reduced resolution lookup table length + "lutminranges" min ranges required before a lookup table is used + "lutscale" scale factor to convert nranges to n LUT bins + "blockfactor" QPIO blocking factor for output pixel arrays + "optbufsize" FIO i/o buffer size for IMIO access to QPOE file, chars + "debuglevel" debug level, 0 for no runtime debug messages + +Environment: + + 'qmfiles' an environment variable listing a set of macro + define files defining the global macros to be + used by QPOE + + 'qmsave' an environment variable defining the name of a + file to be used to store the compiled macros + (defaults to uparm$qpoe.msv). + + +5. INTERFACE PARAMETERS + +# QPSET.H -- User accessible definitions for the QPOE package. + +define SZ_COMMENT 79 # max size comment string +define SZ_DATATYPE 79 # max size datatype name string + +# QPOE Read-Write Parameters. +define QPOE_BLOCKFACTOR 1 # blocking factor for pixel arrays +define QPOE_BUCKETLEN 2 # event list bucket length, nevents +define QPOE_CACHESIZE 3 # lfile (buffer) cache size, nlfiles +define QPOE_DATABUFLEN 4 # QPEX data buffer length, chars +define QPOE_DEBUGLEVEL 5 # debug level (0 = no messages) +define QPOE_DEFLUTLEN 6 # default lookup table length (bins) +define QPOE_INDEXLEN 7 # symbol table hash index length +define QPOE_LUTMINRANGES 8 # min ranges before using LUT +define QPOE_LUTSCALE 9 # scale nranges to LUT bins +define QPOE_MAXFRLUTLEN 10 # max full-res LUT length +define QPOE_MAXLFILES 11 # max lfiles in datafile +define QPOE_MAXPUSHBACK 12 # max amount of pushed back macro data +define QPOE_MAXRRLUTLEN 13 # max reduced-res LUT length +define QPOE_OPTBUFSIZE 14 # optimum buffer size for IMIO/QPF/FIO +define QPOE_PAGESIZE 15 # page size of datafile, bytes +define QPOE_PROGBUFLEN 16 # QPEX program buffer length, ints +define QPOE_SBUFSIZE 17 # symtab string buf size, chars (init) +define QPOE_STABLEN 18 # symtab data area size, su (init) +define QPOE_NODEFFILT 19 # disable use of default filter +define QPOE_NODEFMASK 20 # disable use of default mask + +# QPOE Read-Only Parameters. +define QPOE_FM 21 # FMIO descriptor +define QPOE_MODE 22 # poefile access mode +define QPOE_ST 23 # SYMTAB symbol table descriptor +define QPOE_VERSION 24 # QPOE version number + +# Parameter flags (for qp_addf). +define QPF_NONE (-1) # no flags (0 gives default flags) +define QPF_INHERIT 0002B # copy parameter in a NEW_COPY open + + +# QPIOSET.H -- QPIO User accessible interface parameters. + +define qpio_stati qpiost # (name collision with qpio_seti) + +# Read-Write Parameters. +define QPIO_BLOCKFACTOR 1 # blocking factor for image matrices +define QPIO_BUCKETLEN 2 # event list bucket size, nevents +define QPIO_DEBUG 3 # debug level, debug=0 for no messages +define QPIO_EVXOFF 4 # short offset of X field of event +define QPIO_EVYOFF 5 # short offset of Y field of event +define QPIO_EX 6 # QPEX descriptor (event attr. filter) +define QPIO_NODEFFILT 7 # disable use of default filter +define QPIO_NODEFMASK 8 # disable use of default mask +define QPIO_NOINDEX 9 # flag to disable use of index +define QPIO_OPTBUFSIZE 10 # optimum buffer size for IMIO/QPF/FIO +define QPIO_PL 11 # PLIO descriptor (pixel mask) + +# Read-Only Parameters. +define QPIO_EVENTLEN 12 # length of event struct, shorts +define QPIO_FD 13 # file descriptor of event list lfile +define QPIO_INDEXLEN 14 # event list index length (0=noindex) +define QPIO_IXXOFF 15 # short offset of X field used in index +define QPIO_IXYOFF 16 # short offset of Y field used in index +define QPIO_LF 17 # lfile in which event list is stored +define QPIO_MASKP 18 # char pointer to mask-name buffer +define QPIO_MAXEVP 19 # pointer to MAX-event fields struct +define QPIO_MINEVP 20 # pointer to MIN-event fields struct +define QPIO_NCOLS 21 # number of columns in image +define QPIO_NLINES 22 # number of lines in image +define QPIO_PARAMP 23 # char pointer to param-name buffer +define QPIO_QP 24 # backpointer to QPOE descriptor diff --git a/sys/qpoe/gen/mkpkg b/sys/qpoe/gen/mkpkg new file mode 100644 index 00000000..8c08a1f7 --- /dev/null +++ b/sys/qpoe/gen/mkpkg @@ -0,0 +1,47 @@ +# Update the generically expanded files in libex.a. + +$checkout libex.a lib$ +$update libex.a +$checkin libex.a lib$ +$exit + +libex.a: + qpaddb.x ../qpoe.h + qpaddc.x ../qpoe.h + qpaddd.x ../qpoe.h + qpaddi.x ../qpoe.h + qpaddl.x ../qpoe.h + qpaddr.x ../qpoe.h + qpadds.x ../qpoe.h + qpaddx.x ../qpoe.h + qpexattrld.x ../qpex.h <ctype.h> <mach.h> + qpexattrli.x ../qpex.h <ctype.h> <mach.h> + qpexattrlr.x ../qpex.h <ctype.h> <mach.h> + qpexcoded.x ../qpex.h <mach.h> + qpexcodei.x ../qpex.h <mach.h> + qpexcoder.x ../qpex.h <mach.h> + qpexparsed.x ../qpex.h <ctype.h> <mach.h> + qpexparsei.x ../qpex.h <ctype.h> <mach.h> + qpexparser.x ../qpex.h <ctype.h> <mach.h> + qpexsubd.x ../qpex.h <mach.h> + qpexsubi.x ../qpex.h <mach.h> + qpexsubr.x ../qpex.h <mach.h> + qpgetc.x ../qpoe.h + qpgetd.x ../qpoe.h + qpgeti.x ../qpoe.h + qpgetl.x ../qpoe.h + qpgetr.x ../qpoe.h + qpgets.x ../qpoe.h + qpiogetev.x ../qpio.h <pmset.h> + qpiorpixi.x ../qpio.h <mach.h> + qpiorpixs.x ../qpio.h <mach.h> + qpputc.x ../qpoe.h + qpputd.x ../qpoe.h + qpputi.x ../qpoe.h + qpputl.x ../qpoe.h + qpputr.x ../qpoe.h + qpputs.x ../qpoe.h + qprlmerged.x ../qpex.h <mach.h> + qprlmergei.x ../qpex.h <mach.h> + qprlmerger.x ../qpex.h <mach.h> + ; diff --git a/sys/qpoe/gen/qpaddb.x b/sys/qpoe/gen/qpaddb.x new file mode 100644 index 00000000..1291824a --- /dev/null +++ b/sys/qpoe/gen/qpaddb.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addb (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +bool value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_BOOL] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putb (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddc.x b/sys/qpoe/gen/qpaddc.x new file mode 100644 index 00000000..64264e20 --- /dev/null +++ b/sys/qpoe/gen/qpaddc.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addc (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_CHAR] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putc (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddd.x b/sys/qpoe/gen/qpaddd.x new file mode 100644 index 00000000..61db744e --- /dev/null +++ b/sys/qpoe/gen/qpaddd.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addd (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +double value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_DOUBLE] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putd (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddi.x b/sys/qpoe/gen/qpaddi.x new file mode 100644 index 00000000..47d746c6 --- /dev/null +++ b/sys/qpoe/gen/qpaddi.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addi (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +int value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_INT] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_puti (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddl.x b/sys/qpoe/gen/qpaddl.x new file mode 100644 index 00000000..f5e0cac2 --- /dev/null +++ b/sys/qpoe/gen/qpaddl.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addl (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +long value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_LONG] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putl (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddr.x b/sys/qpoe/gen/qpaddr.x new file mode 100644 index 00000000..ec367ab7 --- /dev/null +++ b/sys/qpoe/gen/qpaddr.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addr (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +real value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_REAL] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putr (qp, param, value) +end diff --git a/sys/qpoe/gen/qpadds.x b/sys/qpoe/gen/qpadds.x new file mode 100644 index 00000000..67036fda --- /dev/null +++ b/sys/qpoe/gen/qpadds.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_adds (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +short value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_SHORT] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_puts (qp, param, value) +end diff --git a/sys/qpoe/gen/qpaddx.x b/sys/qpoe/gen/qpaddx.x new file mode 100644 index 00000000..d147748e --- /dev/null +++ b/sys/qpoe/gen/qpaddx.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_addx (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +complex value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_COMPLEX] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_putx (qp, param, value) +end diff --git a/sys/qpoe/gen/qpexattrld.x b/sys/qpoe/gen/qpexattrld.x new file mode 100644 index 00000000..5954cbe4 --- /dev/null +++ b/sys/qpoe/gen/qpexattrld.x @@ -0,0 +1,127 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <ctype.h> +include "../qpex.h" + +# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a +# binary range list of the indicated type. This range list is a simplified +# version of the original filter expression, which may have contained +# multiple fields, some negated or overlapping, in any order, subsequently +# modified or deleted with qpex_modfilter, etc. The final resultant range +# list is ordered and consists of discreet nonoverlapping ranges. +# +# Upon input the variables XS and XE should either point to a pair of +# preallocated buffers of length XLEN, or they should be set to NULL. +# The routine will reallocate the buffers as necessary to allow for long +# range lists, updating XLEN so that it always contains the actual length +# of the arrays (which may not be completely full). Each list element +# consists of a pair of values (xs[i],xe[i]) defining the start and end +# points of the range. If xs[1] is INDEF the range is open to the left, +# if xe[nranges] is INDEF the range is open to the right. The number of +# ranges output is returned as the function value. + +int procedure qpex_attrld (ex, attribute, xs, xe, xlen) + +pointer ex #I QPEX descriptor +char attribute[ARB] #I attribute name +pointer xs #U pointer to array of start values +pointer xe #U pointer to array of end values +int xlen #U length of xs/xe arrays + +pointer ps, pe, qs, qe +pointer sp, expr, ip, ep +int plen, qlen, np, nq, nx +int neterms, nchars, maxch +int qpex_getattribute(), qpex_parsed(), qp_rlmerged() + +begin + call smark (sp) + + # Get attribute filter expression. In the unlikely event that the + # expression is too large to fit in our buffer, repeat with a buffer + # twice as large until it fits. + + maxch = DEF_SZEXPRBUF + nchars = 0 + + repeat { + maxch = maxch * 2 + call salloc (expr, maxch, TY_CHAR) + nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch) + if (nchars <= 0) + break + } until (nchars < maxch) + + # Parse expression to produce a range list. If the expression + # contains multiple eterms each is parsed separately and merged + # into the final output range list. + + nx = 0 + neterms = 0 + + if (nchars > 0) { + # Get range list storage. + plen = DEF_XLEN + call malloc (ps, plen, TY_DOUBLE) + call malloc (pe, plen, TY_DOUBLE) + qlen = DEF_XLEN + call malloc (qs, qlen, TY_DOUBLE) + call malloc (qe, qlen, TY_DOUBLE) + + # Parse each subexpression and merge into output range list. + for (ip=expr; Memc[ip] != EOS; ) { + # Get next subexpression. + while (IS_WHITE (Memc[ip])) + ip = ip + 1 + for (ep=ip; Memc[ip] != EOS; ip=ip+1) + if (Memc[ip] == ';') { + Memc[ip] = EOS + ip = ip + 1 + break + } + if (Memc[ep] == EOS) + break + + # Copy output range list to X list temporary. + if (max(nx,1) > plen) { + plen = max(xlen,1) + call realloc (ps, plen, TY_DOUBLE) + call realloc (pe, plen, TY_DOUBLE) + } + if (neterms <= 0) { + Memd[ps] = LEFTD + Memd[pe] = RIGHTD + np = 1 + } else { + call amovd (Memd[xs], Memd[ps], nx) + call amovd (Memd[xe], Memd[pe], nx) + np = nx + } + + # Parse next eterm into Y list temporary. + nq = qpex_parsed (Memc[ep], qs, qe, qlen) + + # Merge the X and Y lists, leaving result in output list. + nx = qp_rlmerged (xs,xe,xlen, + Memd[ps], Memd[pe], np, Memd[qs], Memd[qe], nq) + + neterms = neterms + 1 + } + + # Free temporary range list storage. + call mfree (ps, TY_DOUBLE); call mfree (pe, TY_DOUBLE) + call mfree (qs, TY_DOUBLE); call mfree (qe, TY_DOUBLE) + + # Convert LEFT/RIGHT magic values to INDEF. + if (nx > 0) { + if (IS_LEFTD (Memd[xs])) + Memd[xs] = INDEFD + if (IS_RIGHTD (Memd[xe+nx-1])) + Memd[xe+nx-1] = INDEFD + } + } + + call sfree (sp) + return (nx) +end diff --git a/sys/qpoe/gen/qpexattrli.x b/sys/qpoe/gen/qpexattrli.x new file mode 100644 index 00000000..706aecc8 --- /dev/null +++ b/sys/qpoe/gen/qpexattrli.x @@ -0,0 +1,127 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <ctype.h> +include "../qpex.h" + +# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a +# binary range list of the indicated type. This range list is a simplified +# version of the original filter expression, which may have contained +# multiple fields, some negated or overlapping, in any order, subsequently +# modified or deleted with qpex_modfilter, etc. The final resultant range +# list is ordered and consists of discreet nonoverlapping ranges. +# +# Upon input the variables XS and XE should either point to a pair of +# preallocated buffers of length XLEN, or they should be set to NULL. +# The routine will reallocate the buffers as necessary to allow for long +# range lists, updating XLEN so that it always contains the actual length +# of the arrays (which may not be completely full). Each list element +# consists of a pair of values (xs[i],xe[i]) defining the start and end +# points of the range. If xs[1] is INDEF the range is open to the left, +# if xe[nranges] is INDEF the range is open to the right. The number of +# ranges output is returned as the function value. + +int procedure qpex_attrli (ex, attribute, xs, xe, xlen) + +pointer ex #I QPEX descriptor +char attribute[ARB] #I attribute name +pointer xs #U pointer to array of start values +pointer xe #U pointer to array of end values +int xlen #U length of xs/xe arrays + +pointer ps, pe, qs, qe +pointer sp, expr, ip, ep +int plen, qlen, np, nq, nx +int neterms, nchars, maxch +int qpex_getattribute(), qpex_parsei(), qp_rlmergei() + +begin + call smark (sp) + + # Get attribute filter expression. In the unlikely event that the + # expression is too large to fit in our buffer, repeat with a buffer + # twice as large until it fits. + + maxch = DEF_SZEXPRBUF + nchars = 0 + + repeat { + maxch = maxch * 2 + call salloc (expr, maxch, TY_CHAR) + nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch) + if (nchars <= 0) + break + } until (nchars < maxch) + + # Parse expression to produce a range list. If the expression + # contains multiple eterms each is parsed separately and merged + # into the final output range list. + + nx = 0 + neterms = 0 + + if (nchars > 0) { + # Get range list storage. + plen = DEF_XLEN + call malloc (ps, plen, TY_INT) + call malloc (pe, plen, TY_INT) + qlen = DEF_XLEN + call malloc (qs, qlen, TY_INT) + call malloc (qe, qlen, TY_INT) + + # Parse each subexpression and merge into output range list. + for (ip=expr; Memc[ip] != EOS; ) { + # Get next subexpression. + while (IS_WHITE (Memc[ip])) + ip = ip + 1 + for (ep=ip; Memc[ip] != EOS; ip=ip+1) + if (Memc[ip] == ';') { + Memc[ip] = EOS + ip = ip + 1 + break + } + if (Memc[ep] == EOS) + break + + # Copy output range list to X list temporary. + if (max(nx,1) > plen) { + plen = max(xlen,1) + call realloc (ps, plen, TY_INT) + call realloc (pe, plen, TY_INT) + } + if (neterms <= 0) { + Memi[ps] = LEFTI + Memi[pe] = RIGHTI + np = 1 + } else { + call amovi (Memi[xs], Memi[ps], nx) + call amovi (Memi[xe], Memi[pe], nx) + np = nx + } + + # Parse next eterm into Y list temporary. + nq = qpex_parsei (Memc[ep], qs, qe, qlen) + + # Merge the X and Y lists, leaving result in output list. + nx = qp_rlmergei (xs,xe,xlen, + Memi[ps], Memi[pe], np, Memi[qs], Memi[qe], nq) + + neterms = neterms + 1 + } + + # Free temporary range list storage. + call mfree (ps, TY_INT); call mfree (pe, TY_INT) + call mfree (qs, TY_INT); call mfree (qe, TY_INT) + + # Convert LEFT/RIGHT magic values to INDEF. + if (nx > 0) { + if (IS_LEFTI (Memi[xs])) + Memi[xs] = INDEFI + if (IS_RIGHTI (Memi[xe+nx-1])) + Memi[xe+nx-1] = INDEFI + } + } + + call sfree (sp) + return (nx) +end diff --git a/sys/qpoe/gen/qpexattrlr.x b/sys/qpoe/gen/qpexattrlr.x new file mode 100644 index 00000000..c13a7511 --- /dev/null +++ b/sys/qpoe/gen/qpexattrlr.x @@ -0,0 +1,127 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <ctype.h> +include "../qpex.h" + +# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a +# binary range list of the indicated type. This range list is a simplified +# version of the original filter expression, which may have contained +# multiple fields, some negated or overlapping, in any order, subsequently +# modified or deleted with qpex_modfilter, etc. The final resultant range +# list is ordered and consists of discreet nonoverlapping ranges. +# +# Upon input the variables XS and XE should either point to a pair of +# preallocated buffers of length XLEN, or they should be set to NULL. +# The routine will reallocate the buffers as necessary to allow for long +# range lists, updating XLEN so that it always contains the actual length +# of the arrays (which may not be completely full). Each list element +# consists of a pair of values (xs[i],xe[i]) defining the start and end +# points of the range. If xs[1] is INDEF the range is open to the left, +# if xe[nranges] is INDEF the range is open to the right. The number of +# ranges output is returned as the function value. + +int procedure qpex_attrlr (ex, attribute, xs, xe, xlen) + +pointer ex #I QPEX descriptor +char attribute[ARB] #I attribute name +pointer xs #U pointer to array of start values +pointer xe #U pointer to array of end values +int xlen #U length of xs/xe arrays + +pointer ps, pe, qs, qe +pointer sp, expr, ip, ep +int plen, qlen, np, nq, nx +int neterms, nchars, maxch +int qpex_getattribute(), qpex_parser(), qp_rlmerger() + +begin + call smark (sp) + + # Get attribute filter expression. In the unlikely event that the + # expression is too large to fit in our buffer, repeat with a buffer + # twice as large until it fits. + + maxch = DEF_SZEXPRBUF + nchars = 0 + + repeat { + maxch = maxch * 2 + call salloc (expr, maxch, TY_CHAR) + nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch) + if (nchars <= 0) + break + } until (nchars < maxch) + + # Parse expression to produce a range list. If the expression + # contains multiple eterms each is parsed separately and merged + # into the final output range list. + + nx = 0 + neterms = 0 + + if (nchars > 0) { + # Get range list storage. + plen = DEF_XLEN + call malloc (ps, plen, TY_REAL) + call malloc (pe, plen, TY_REAL) + qlen = DEF_XLEN + call malloc (qs, qlen, TY_REAL) + call malloc (qe, qlen, TY_REAL) + + # Parse each subexpression and merge into output range list. + for (ip=expr; Memc[ip] != EOS; ) { + # Get next subexpression. + while (IS_WHITE (Memc[ip])) + ip = ip + 1 + for (ep=ip; Memc[ip] != EOS; ip=ip+1) + if (Memc[ip] == ';') { + Memc[ip] = EOS + ip = ip + 1 + break + } + if (Memc[ep] == EOS) + break + + # Copy output range list to X list temporary. + if (max(nx,1) > plen) { + plen = max(xlen,1) + call realloc (ps, plen, TY_REAL) + call realloc (pe, plen, TY_REAL) + } + if (neterms <= 0) { + Memr[ps] = LEFTR + Memr[pe] = RIGHTR + np = 1 + } else { + call amovr (Memr[xs], Memr[ps], nx) + call amovr (Memr[xe], Memr[pe], nx) + np = nx + } + + # Parse next eterm into Y list temporary. + nq = qpex_parser (Memc[ep], qs, qe, qlen) + + # Merge the X and Y lists, leaving result in output list. + nx = qp_rlmerger (xs,xe,xlen, + Memr[ps], Memr[pe], np, Memr[qs], Memr[qe], nq) + + neterms = neterms + 1 + } + + # Free temporary range list storage. + call mfree (ps, TY_REAL); call mfree (pe, TY_REAL) + call mfree (qs, TY_REAL); call mfree (qe, TY_REAL) + + # Convert LEFT/RIGHT magic values to INDEF. + if (nx > 0) { + if (IS_LEFTR (Memr[xs])) + Memr[xs] = INDEFR + if (IS_RIGHTR (Memr[xe+nx-1])) + Memr[xe+nx-1] = INDEFR + } + } + + call sfree (sp) + return (nx) +end diff --git a/sys/qpoe/gen/qpexcoded.x b/sys/qpoe/gen/qpexcoded.x new file mode 100644 index 00000000..63ec2541 --- /dev/null +++ b/sys/qpoe/gen/qpexcoded.x @@ -0,0 +1,370 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given +# expression. The new code is appended to the current compiled program, +# adding additional constraints which a data event will have to meet to +# pass the filter. + +int procedure qpex_codegend (ex, atname, assignop, expr, offset, dtype) + +pointer ex #I qpex descriptor +char atname[ARB] #I attribute name (for expr regeneration) +char assignop[ARB] #I "=" or "+=" (for expr regeneration) +char expr[ARB] #I expression to be compiled +int offset #I typed offset of referenced attribute +int dtype #I datatype of referenced attribute + +int nbins, bin, xp +pointer lt, lut, lutx, pb +double x1, x2, xmin, xmax +int xlen, nranges, n_nranges, level, opcode, ip, i +pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev + +double sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS] +pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS] +int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS] +int sv_nbins[MAX_LEVELS] + +double xoffset, xscale +double sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS] +int d_x1, d_x2 +int qpex_refd() + +bool fp_equald() + + +int qpex_parsed() +int stridxs(), btoi(), qpex_sublistd() +pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos() +errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parsed + +string qpexwarn "QPEX Warning" +define error_ 91 +define next_ 92 +define null_ 93 +define resume_ 94 +define bbmask_ 95 +define continue_ 96 +define XS Memd[xs+($1)-1] +define XE Memd[xe+($1)-1] + +begin + pb = EX_PB(ex) + + # Save the program state in case we have to abort. + call qpex_mark (ex, pb_save, db_save) + + # Allocate and initialize a new expression term descriptor, linking + # it onto the tail of the ETTERMs list. + + et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT) + + ET_ATTTYPE(et) = dtype + ET_ATTOFF(et) = offset + ET_ATNAME(et) = qpex_dbpstr (ex, atname) + ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop) + ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr) + ET_PROGPTR(et) = qpex_pbpos (ex) + ET_DELETED(et) = NO + + prev = EX_ETTAIL(ex) + if (prev != NULL) + ET_NEXT(prev) = et + ET_NEXT(et) = NULL + EX_ETTAIL(ex) = et + if (EX_ETHEAD(ex) == NULL) + EX_ETHEAD(ex) = et + + ip = stridxs ("%", expr) + # Bitmask tests are meaningless for floating point data. + if (ip > 0) { + call eprintf ("%s: bitmasks not permitted for floating data\n") + call pargstr (qpexwarn) + goto error_ + } + + # Compile a general range list expression. The basic procedure is + # to parse the expression to produce an optimized binary range list, + # then either compile the range list as an explicit series of + # instructions or as a lookup table, depending upon the number of + # ranges. + + xlen = DEF_XLEN + call malloc (xs_buf, xlen, TY_DOUBLE) + call malloc (xe_buf, xlen, TY_DOUBLE) + + # Convert expr to a binary range list and set up the initial context. + # Ensure that the range list buffers are large enough to hold any + # sublists extracted during compilation. + + nranges = qpex_parsed (expr, xs_buf, xe_buf, xlen) + if (xlen < nranges * 2) { + xlen = nranges * 2 + call realloc (xs_buf, xlen, TY_DOUBLE) + call realloc (xe_buf, xlen, TY_DOUBLE) + } + + xs = xs_buf + xe = xe_buf + level = 0 + + repeat { +next_ + # Compile a new range list (or sublist). + if (nranges <= 0) { + # This shouldn't happen. +null_ call eprintf ("%s: null range list\n") + call pargstr (qpexwarn) + call qpex_pbpin (ex, PASS, 0, 0, 0) + + } else if (nranges == 1) { + # Output an instruction to load the data, perform the range + # test, and conditionally exit all in a single instruction. + + x1 = XS(1); x2 = XE(1) + d_x1 = qpex_refd (ex, x1) + d_x2 = qpex_refd (ex, x2) + + if (dtype == TY_SHORT) { + if (IS_LEFTD(x1) && IS_RIGHTD(x2)) + ; # pass everything (no tests) + else if (IS_LEFTD(x1)) + call qpex_pbpin (ex, LEQXS, offset, d_x2, 0) + else if (IS_RIGHTD(x2)) + call qpex_pbpin (ex, GEQXS, offset, d_x1, 0) + else if (fp_equald (x1, x2)) + call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2) + } else { + if (IS_LEFTD(x1) && IS_RIGHTD(x2)) + ; # pass everything (no tests) + else if (IS_LEFTD(x1)) + call qpex_pbpin (ex, LEQXD, offset, d_x2, 0) + else if (IS_RIGHTD(x2)) + call qpex_pbpin (ex, GEQXD, offset, d_x1, 0) + else if (fp_equald (x1, x2)) + call qpex_pbpin (ex, EQLXD, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXD, offset, d_x1, d_x2) + } + + } else if (nranges < EX_LUTMINRANGES(ex)) { + # If the number of ranges to be tested for the data is small, + # compile explicit code to perform the range tests directly. + # Otherwise skip forward and compile a lookup table instead. + # In either case, the function of the instructions compiled + # is to test the data loaded into the register above, setting + # the value of PASS to true if the data lies in any of the + # indicated ranges. + + # Check for !X, which is indicated in range list form by a + # two element list bracketing the X on each side. + + if (nranges == 2) + if (IS_LEFTD(XS(1)) && IS_RIGHTD(XE(2))) + if (fp_equald (XE(1), XS(2))) { + call qpex_pbpin (ex, NEQXD, offset, + qpex_refd(ex,XE(1)), 0) + goto resume_ + } + + # If at level zero, output instruction to load data into + # register and initialize PASS to false. Don't bother if + # compiling a subprogram, as these operations will already + # have been performed by the caller. + + if (level == 0) { + opcode = LDDD + call qpex_pbpin (ex, opcode, offset, 0, 0) + } + + # Compile a series of equality or range tests. + do i = 1, nranges { + x1 = XS(i); x2 = XE(i) + d_x1 = qpex_refd (ex, x1) + d_x2 = qpex_refd (ex, x2) + + if (IS_LEFTD(x1)) + call qpex_pbpin (ex, LEQD, d_x2, 0, 0) + else if (IS_RIGHTD(x2)) + call qpex_pbpin (ex, GEQD, d_x1, 0, 0) + else if (fp_equald (x1, x2)) + call qpex_pbpin (ex, EQLD, d_x1, d_x2, 0) + else + call qpex_pbpin (ex, RNGD, d_x1, d_x2, 0) + } + + # Compile a test and exit instruction. + call qpex_pbpin (ex, XIFF, 0, 0, 0) + + } else { + # Compile a lookup table test. Lookup tables may be + # either compressed or fully resolved. If compressed + # (the resolution of the table is less than that of the + # range data, e.g., for floating point lookup tables) a + # LUT bin may have as its value, in addition to the + # usual 0 or 1, the address of an interpreter subprogram + # to be executed to test data values mapping to that bin. + # The subprogram pointed to may in turn be another lookup + # table, hence in the general case a tree of lookup tables + # and little code segments may be compiled to implement + # a complex range list test. + + # Get the data range of the lookup table. + xmin = XS(1) + if (IS_LEFTD(xmin)) + xmin = XE(1) + xmax = XE(nranges) + if (IS_RIGHTD(xmax)) + xmax = XS(nranges) + + # Get the lookup table size. Use a fully resolved table + # if the data is integer and the number of bins required + # is modest. + + nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex)) + + # Determine the mapping from data space to table space. + xoffset = xmin + xscale = nbins / (xmax - xmin) + + # Allocate and initialize the lookup table descriptor. + lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT) + call calloc (lut, nbins, TY_SHORT) + + LT_NEXT(lt) = EX_LTHEAD(ex) + EX_LTHEAD(ex) = lt + LT_TYPE(lt) = TY_DOUBLE + LT_LUTP(lt) = lut + LT_NBINS(lt) = nbins + LT_D0(lt) = xoffset + LT_DS(lt) = xscale + LT_LEFT(lt) = btoi (IS_LEFTD(XS(1))) + LT_RIGHT(lt) = btoi (IS_RIGHTD(XE(nranges))) + + # Compile the LUTX test instruction. Save a back pointer + # to the instruction so that we can edit the jump field in + # case a subprogram is compiled after the LUTXt. + + lutx = qpex_pbpos (ex) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, LUTXS, offset, lt, 0) + else + call qpex_pbpin (ex, LUTXD, offset, lt, 0) + + xp = 1 + bin = 1 +continue_ + n_xs = xs + nranges + n_xe = xe + nranges + + # Initialize the lookup table. + do i = bin, nbins { + x1 = (i-1) / xscale + xoffset + x2 = i / xscale + xoffset + + # Get sub-rangelist for range x1:x2. + n_nranges = qpex_sublistd (x1, x2, + Memd[xs], Memd[xe], nranges, xp, + Memd[n_xs], Memd[n_xe]) + + if (n_nranges <= 0) { + Mems[lut+i-1] = 0 + + } else if (n_nranges == 1 && IS_LEFTD(Memd[n_xs]) && + IS_RIGHTD(Memd[n_xe])) { + + Mems[lut+i-1] = 1 + + } else { + # Compile the sub-rangelist as a subprogram. + + # First set the LUT bin to point to the subprogram. + # We cannot use the IP directly here since the LUT + # bins are short integer, so store the offset into + # the pb instead (guaranteed to be >= 4). + + Mems[lut+i-1] = qpex_pbpos(ex) - pb + + # Push a new context. + level = level + 1 + if (level > MAX_LEVELS) { + call eprintf ("%s: ") + call pargstr (qpexwarn) + call eprintf ("Excessive LUT nesting\n") + goto error_ + } + + # Save current LUT compilation context. + sv_xs[level] = xs + sv_xe[level] = xe + sv_xp[level] = xp + sv_xoffset[level] = xoffset + sv_xscale[level] = xscale + sv_nranges[level] = nranges + sv_lt[level] = lt + sv_bin[level] = i + sv_nbins[level] = nbins + sv_lut[level] = lut + sv_lutx[level] = lutx + + # Set up context for the new rangelist. + xs = n_xs + xe = n_xe + nranges = n_nranges + + goto next_ + } + } + + # Compile a test and exit instruction if the LUT calls any + # subprograms. + + if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION) + call qpex_pbpin (ex, XIFF, 0, 0, 0) + } +resume_ + # Resume lookup table compilation if exiting due to LUT-bin + # subprogram compilation. + + if (level > 0) { + # Pop saved context. + xs = sv_xs[level] + xe = sv_xe[level] + xp = sv_xp[level] + xoffset = sv_xoffset[level] + xscale = sv_xscale[level] + nranges = sv_nranges[level] + lt = sv_lt[level] + bin = sv_bin[level] + nbins = sv_nbins[level] + lut = sv_lut[level] + lutx = sv_lutx[level] + + # Compile a return from subprogram. + call qpex_pbpin (ex, RET, 0, 0, 0) + + # Patch up the original LUTX instruction to jump over the + # subprogram we have just finished compiling. + + IARG3(lutx) = qpex_pbpos (ex) + + # Resume compilation at the next LUT bin. + bin = bin + 1 + level = level - 1 + goto continue_ + } + } until (level <= 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION + + return (OK) +error_ + call qpex_free (ex, pb_save, db_save) + return (ERR) +end diff --git a/sys/qpoe/gen/qpexcodei.x b/sys/qpoe/gen/qpexcodei.x new file mode 100644 index 00000000..db8cbc72 --- /dev/null +++ b/sys/qpoe/gen/qpexcodei.x @@ -0,0 +1,423 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given +# expression. The new code is appended to the current compiled program, +# adding additional constraints which a data event will have to meet to +# pass the filter. + +int procedure qpex_codegeni (ex, atname, assignop, expr, offset, dtype) + +pointer ex #I qpex descriptor +char atname[ARB] #I attribute name (for expr regeneration) +char assignop[ARB] #I "=" or "+=" (for expr regeneration) +char expr[ARB] #I expression to be compiled +int offset #I typed offset of referenced attribute +int dtype #I datatype of referenced attribute + +int nbins, bin, xp +pointer lt, lut, lutx, pb +int x1, x2, xmin, xmax +int xlen, nranges, n_nranges, level, opcode, ip, i +pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev + +int sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS] +pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS] +int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS] +int sv_nbins[MAX_LEVELS] + +int d_x1, d_x2 +real xoffset, xscale +real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS] + +define fp_equali($1==$2) + +bool complement +int maskval +int qp_ctoi() + +int qpex_parsei() +int stridxs(), btoi(), qpex_sublisti() +pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos() +errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parsei + +string qpexwarn "QPEX Warning" +define error_ 91 +define next_ 92 +define null_ 93 +define resume_ 94 +define bbmask_ 95 +define continue_ 96 +define XS Memi[xs+($1)-1] +define XE Memi[xe+($1)-1] + +begin + pb = EX_PB(ex) + + # Save the program state in case we have to abort. + call qpex_mark (ex, pb_save, db_save) + + # Allocate and initialize a new expression term descriptor, linking + # it onto the tail of the ETTERMs list. + + et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT) + + ET_ATTTYPE(et) = dtype + ET_ATTOFF(et) = offset + ET_ATNAME(et) = qpex_dbpstr (ex, atname) + ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop) + ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr) + ET_PROGPTR(et) = qpex_pbpos (ex) + ET_DELETED(et) = NO + + prev = EX_ETTAIL(ex) + if (prev != NULL) + ET_NEXT(prev) = et + ET_NEXT(et) = NULL + EX_ETTAIL(ex) = et + if (EX_ETHEAD(ex) == NULL) + EX_ETHEAD(ex) = et + + ip = stridxs ("%", expr) + # Attempt to compile a bitmask test if `%' is found in the + # expression. Since bitmasks cannot be mixed with range list + # expressions, this case is handled separately. + + if (ip > 0) { + complement = false + level = 0 + + # Parse expression (very limited for this case). + for (ip=1; expr[ip] != EOS; ip=ip+1) { + switch (expr[ip]) { + case '!': + complement = !complement + case '(', '[': + level = level + 1 + case ')', ']': + level = level - 1 + case '%': + ip = ip + 1 + if (qp_ctoi (expr, ip, maskval) < 0) + goto bbmask_ + else + ip = ip - 1 + default: + goto bbmask_ + } + } + + # Verify paren level, handle errors. + if (level != 0) { +bbmask_ call eprintf ("%s: bad bitmask expression `%s'\n") + call pargstr (qpexwarn) + call pargstr (expr) + goto error_ + } + + # Compile the bitmask test. + if (complement) + maskval = not(maskval) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, BTTXS, offset, maskval, 0) + else + call qpex_pbpin (ex, BTTXI, offset, maskval, 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = 1 + return (OK) + } + + # Compile a general range list expression. The basic procedure is + # to parse the expression to produce an optimized binary range list, + # then either compile the range list as an explicit series of + # instructions or as a lookup table, depending upon the number of + # ranges. + + xlen = DEF_XLEN + call malloc (xs_buf, xlen, TY_INT) + call malloc (xe_buf, xlen, TY_INT) + + # Convert expr to a binary range list and set up the initial context. + # Ensure that the range list buffers are large enough to hold any + # sublists extracted during compilation. + + nranges = qpex_parsei (expr, xs_buf, xe_buf, xlen) + if (xlen < nranges * 2) { + xlen = nranges * 2 + call realloc (xs_buf, xlen, TY_INT) + call realloc (xe_buf, xlen, TY_INT) + } + + xs = xs_buf + xe = xe_buf + level = 0 + + repeat { +next_ + # Compile a new range list (or sublist). + if (nranges <= 0) { + # This shouldn't happen. +null_ call eprintf ("%s: null range list\n") + call pargstr (qpexwarn) + call qpex_pbpin (ex, PASS, 0, 0, 0) + + } else if (nranges == 1) { + # Output an instruction to load the data, perform the range + # test, and conditionally exit all in a single instruction. + + x1 = XS(1); x2 = XE(1) + d_x1 = x1 + d_x2 = x2 + + if (dtype == TY_SHORT) { + if (IS_LEFTI(x1) && IS_RIGHTI(x2)) + ; # pass everything (no tests) + else if (IS_LEFTI(x1)) + call qpex_pbpin (ex, LEQXS, offset, d_x2, 0) + else if (IS_RIGHTI(x2)) + call qpex_pbpin (ex, GEQXS, offset, d_x1, 0) + else if (fp_equali (x1, x2)) + call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2) + } else { + if (IS_LEFTI(x1) && IS_RIGHTI(x2)) + ; # pass everything (no tests) + else if (IS_LEFTI(x1)) + call qpex_pbpin (ex, LEQXI, offset, d_x2, 0) + else if (IS_RIGHTI(x2)) + call qpex_pbpin (ex, GEQXI, offset, d_x1, 0) + else if (fp_equali (x1, x2)) + call qpex_pbpin (ex, EQLXI, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXI, offset, d_x1, d_x2) + } + + } else if (nranges < EX_LUTMINRANGES(ex)) { + # If the number of ranges to be tested for the data is small, + # compile explicit code to perform the range tests directly. + # Otherwise skip forward and compile a lookup table instead. + # In either case, the function of the instructions compiled + # is to test the data loaded into the register above, setting + # the value of PASS to true if the data lies in any of the + # indicated ranges. + + # Check for !X, which is indicated in range list form by a + # two element list bracketing the X on each side. + + if (nranges == 2) + if (IS_LEFTI(XS(1)) && IS_RIGHTI(XE(2))) + if (XE(1)+1 == XS(2)-1) { + if (dtype == TY_SHORT) + opcode = NEQXS + else + opcode = NEQXI + call qpex_pbpin (ex, opcode, offset, XE(1)+1, 0) + goto resume_ + } + + # If at level zero, output instruction to load data into + # register and initialize PASS to false. Don't bother if + # compiling a subprogram, as these operations will already + # have been performed by the caller. + + if (level == 0) { + if (dtype == TY_SHORT) + opcode = LDSI + else + opcode = LDII + call qpex_pbpin (ex, opcode, offset, 0, 0) + } + + # Compile a series of equality or range tests. + do i = 1, nranges { + x1 = XS(i); x2 = XE(i) + d_x1 = x1 + d_x2 = x2 + + if (IS_LEFTI(x1)) + call qpex_pbpin (ex, LEQI, d_x2, 0, 0) + else if (IS_RIGHTI(x2)) + call qpex_pbpin (ex, GEQI, d_x1, 0, 0) + else if (fp_equali (x1, x2)) + call qpex_pbpin (ex, EQLI, d_x1, d_x2, 0) + else + call qpex_pbpin (ex, RNGI, d_x1, d_x2, 0) + } + + # Compile a test and exit instruction. + call qpex_pbpin (ex, XIFF, 0, 0, 0) + + } else { + # Compile a lookup table test. Lookup tables may be + # either compressed or fully resolved. If compressed + # (the resolution of the table is less than that of the + # range data, e.g., for floating point lookup tables) a + # LUT bin may have as its value, in addition to the + # usual 0 or 1, the address of an interpreter subprogram + # to be executed to test data values mapping to that bin. + # The subprogram pointed to may in turn be another lookup + # table, hence in the general case a tree of lookup tables + # and little code segments may be compiled to implement + # a complex range list test. + + # Get the data range of the lookup table. + xmin = XS(1) + if (IS_LEFTI(xmin)) + xmin = XE(1) + xmax = XE(nranges) + if (IS_RIGHTI(xmax)) + xmax = XS(nranges) + + # Get the lookup table size. Use a fully resolved table + # if the data is integer and the number of bins required + # is modest. + + nbins = xmax - xmin + 1 + if (nbins > EX_MAXFRLUTLEN(ex)) + nbins = min (EX_MAXRRLUTLEN(ex), + nranges * EX_LUTSCALE(ex)) + + # Determine the mapping from data space to table space. + xoffset = xmin + xscale = nbins / (xmax - xmin + 1) + + # Allocate and initialize the lookup table descriptor. + lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT) + call calloc (lut, nbins, TY_SHORT) + + LT_NEXT(lt) = EX_LTHEAD(ex) + EX_LTHEAD(ex) = lt + LT_TYPE(lt) = TY_INT + LT_LUTP(lt) = lut + LT_NBINS(lt) = nbins + LT_I0(lt) = xoffset + LT_IS(lt) = xscale + LT_LEFT(lt) = btoi (IS_LEFTI(XS(1))) + LT_RIGHT(lt) = btoi (IS_RIGHTI(XE(nranges))) + + # Compile the LUTX test instruction. Save a back pointer + # to the instruction so that we can edit the jump field in + # case a subprogram is compiled after the LUTXt. + + lutx = qpex_pbpos (ex) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, LUTXS, offset, lt, 0) + else + call qpex_pbpin (ex, LUTXI, offset, lt, 0) + + xp = 1 + bin = 1 +continue_ + n_xs = xs + nranges + n_xe = xe + nranges + + # Initialize the lookup table. + do i = bin, nbins { + x1 = (i-1) / xscale + xoffset + x2 = i / xscale + xoffset - 1 + + # Get sub-rangelist for range x1:x2. + n_nranges = qpex_sublisti (x1, x2, + Memi[xs], Memi[xe], nranges, xp, + Memi[n_xs], Memi[n_xe]) + + if (n_nranges <= 0) { + Mems[lut+i-1] = 0 + + } else if (n_nranges == 1 && IS_LEFTI(Memi[n_xs]) && + IS_RIGHTI(Memi[n_xe])) { + + Mems[lut+i-1] = 1 + + } else { + # Compile the sub-rangelist as a subprogram. + + # First set the LUT bin to point to the subprogram. + # We cannot use the IP directly here since the LUT + # bins are short integer, so store the offset into + # the pb instead (guaranteed to be >= 4). + + Mems[lut+i-1] = qpex_pbpos(ex) - pb + + # Push a new context. + level = level + 1 + if (level > MAX_LEVELS) { + call eprintf ("%s: ") + call pargstr (qpexwarn) + call eprintf ("Excessive LUT nesting\n") + goto error_ + } + + # Save current LUT compilation context. + sv_xs[level] = xs + sv_xe[level] = xe + sv_xp[level] = xp + sv_xoffset[level] = xoffset + sv_xscale[level] = xscale + sv_nranges[level] = nranges + sv_lt[level] = lt + sv_bin[level] = i + sv_nbins[level] = nbins + sv_lut[level] = lut + sv_lutx[level] = lutx + + # Set up context for the new rangelist. + xs = n_xs + xe = n_xe + nranges = n_nranges + + goto next_ + } + } + + # Compile a test and exit instruction if the LUT calls any + # subprograms. + + if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION) + call qpex_pbpin (ex, XIFF, 0, 0, 0) + } +resume_ + # Resume lookup table compilation if exiting due to LUT-bin + # subprogram compilation. + + if (level > 0) { + # Pop saved context. + xs = sv_xs[level] + xe = sv_xe[level] + xp = sv_xp[level] + xoffset = sv_xoffset[level] + xscale = sv_xscale[level] + nranges = sv_nranges[level] + lt = sv_lt[level] + bin = sv_bin[level] + nbins = sv_nbins[level] + lut = sv_lut[level] + lutx = sv_lutx[level] + + # Compile a return from subprogram. + call qpex_pbpin (ex, RET, 0, 0, 0) + + # Patch up the original LUTX instruction to jump over the + # subprogram we have just finished compiling. + + IARG3(lutx) = qpex_pbpos (ex) + + # Resume compilation at the next LUT bin. + bin = bin + 1 + level = level - 1 + goto continue_ + } + } until (level <= 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION + + return (OK) +error_ + call qpex_free (ex, pb_save, db_save) + return (ERR) +end diff --git a/sys/qpoe/gen/qpexcoder.x b/sys/qpoe/gen/qpexcoder.x new file mode 100644 index 00000000..30e1d85b --- /dev/null +++ b/sys/qpoe/gen/qpexcoder.x @@ -0,0 +1,368 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given +# expression. The new code is appended to the current compiled program, +# adding additional constraints which a data event will have to meet to +# pass the filter. + +int procedure qpex_codegenr (ex, atname, assignop, expr, offset, dtype) + +pointer ex #I qpex descriptor +char atname[ARB] #I attribute name (for expr regeneration) +char assignop[ARB] #I "=" or "+=" (for expr regeneration) +char expr[ARB] #I expression to be compiled +int offset #I typed offset of referenced attribute +int dtype #I datatype of referenced attribute + +int nbins, bin, xp +pointer lt, lut, lutx, pb +real x1, x2, xmin, xmax +int xlen, nranges, n_nranges, level, opcode, ip, i +pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev + +real sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS] +pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS] +int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS] +int sv_nbins[MAX_LEVELS] + +real d_x1, d_x2 +real xoffset, xscale +real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS] + +bool fp_equalr() + + +int qpex_parser() +int stridxs(), btoi(), qpex_sublistr() +pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos() +errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parser + +string qpexwarn "QPEX Warning" +define error_ 91 +define next_ 92 +define null_ 93 +define resume_ 94 +define bbmask_ 95 +define continue_ 96 +define XS Memr[xs+($1)-1] +define XE Memr[xe+($1)-1] + +begin + pb = EX_PB(ex) + + # Save the program state in case we have to abort. + call qpex_mark (ex, pb_save, db_save) + + # Allocate and initialize a new expression term descriptor, linking + # it onto the tail of the ETTERMs list. + + et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT) + + ET_ATTTYPE(et) = dtype + ET_ATTOFF(et) = offset + ET_ATNAME(et) = qpex_dbpstr (ex, atname) + ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop) + ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr) + ET_PROGPTR(et) = qpex_pbpos (ex) + ET_DELETED(et) = NO + + prev = EX_ETTAIL(ex) + if (prev != NULL) + ET_NEXT(prev) = et + ET_NEXT(et) = NULL + EX_ETTAIL(ex) = et + if (EX_ETHEAD(ex) == NULL) + EX_ETHEAD(ex) = et + + ip = stridxs ("%", expr) + # Bitmask tests are meaningless for floating point data. + if (ip > 0) { + call eprintf ("%s: bitmasks not permitted for floating data\n") + call pargstr (qpexwarn) + goto error_ + } + + # Compile a general range list expression. The basic procedure is + # to parse the expression to produce an optimized binary range list, + # then either compile the range list as an explicit series of + # instructions or as a lookup table, depending upon the number of + # ranges. + + xlen = DEF_XLEN + call malloc (xs_buf, xlen, TY_REAL) + call malloc (xe_buf, xlen, TY_REAL) + + # Convert expr to a binary range list and set up the initial context. + # Ensure that the range list buffers are large enough to hold any + # sublists extracted during compilation. + + nranges = qpex_parser (expr, xs_buf, xe_buf, xlen) + if (xlen < nranges * 2) { + xlen = nranges * 2 + call realloc (xs_buf, xlen, TY_REAL) + call realloc (xe_buf, xlen, TY_REAL) + } + + xs = xs_buf + xe = xe_buf + level = 0 + + repeat { +next_ + # Compile a new range list (or sublist). + if (nranges <= 0) { + # This shouldn't happen. +null_ call eprintf ("%s: null range list\n") + call pargstr (qpexwarn) + call qpex_pbpin (ex, PASS, 0, 0, 0) + + } else if (nranges == 1) { + # Output an instruction to load the data, perform the range + # test, and conditionally exit all in a single instruction. + + x1 = XS(1); x2 = XE(1) + d_x1 = x1 + d_x2 = x2 + + if (dtype == TY_SHORT) { + if (IS_LEFTR(x1) && IS_RIGHTR(x2)) + ; # pass everything (no tests) + else if (IS_LEFTR(x1)) + call qpex_pbpin (ex, LEQXS, offset, d_x2, 0) + else if (IS_RIGHTR(x2)) + call qpex_pbpin (ex, GEQXS, offset, d_x1, 0) + else if (fp_equalr (x1, x2)) + call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2) + } else { + if (IS_LEFTR(x1) && IS_RIGHTR(x2)) + ; # pass everything (no tests) + else if (IS_LEFTR(x1)) + call qpex_pbpin (ex, LEQXR, offset, d_x2, 0) + else if (IS_RIGHTR(x2)) + call qpex_pbpin (ex, GEQXR, offset, d_x1, 0) + else if (fp_equalr (x1, x2)) + call qpex_pbpin (ex, EQLXR, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXR, offset, d_x1, d_x2) + } + + } else if (nranges < EX_LUTMINRANGES(ex)) { + # If the number of ranges to be tested for the data is small, + # compile explicit code to perform the range tests directly. + # Otherwise skip forward and compile a lookup table instead. + # In either case, the function of the instructions compiled + # is to test the data loaded into the register above, setting + # the value of PASS to true if the data lies in any of the + # indicated ranges. + + # Check for !X, which is indicated in range list form by a + # two element list bracketing the X on each side. + + if (nranges == 2) + if (IS_LEFTR(XS(1)) && IS_RIGHTR(XE(2))) + if (fp_equalr (XE(1), XS(2))) { + call qpex_pbpin (ex, NEQXR, offset, XE(1), 0) + goto resume_ + } + + # If at level zero, output instruction to load data into + # register and initialize PASS to false. Don't bother if + # compiling a subprogram, as these operations will already + # have been performed by the caller. + + if (level == 0) { + opcode = LDRR + call qpex_pbpin (ex, opcode, offset, 0, 0) + } + + # Compile a series of equality or range tests. + do i = 1, nranges { + x1 = XS(i); x2 = XE(i) + d_x1 = x1 + d_x2 = x2 + + if (IS_LEFTR(x1)) + call qpex_pbpin (ex, LEQR, d_x2, 0, 0) + else if (IS_RIGHTR(x2)) + call qpex_pbpin (ex, GEQR, d_x1, 0, 0) + else if (fp_equalr (x1, x2)) + call qpex_pbpin (ex, EQLR, d_x1, d_x2, 0) + else + call qpex_pbpin (ex, RNGR, d_x1, d_x2, 0) + } + + # Compile a test and exit instruction. + call qpex_pbpin (ex, XIFF, 0, 0, 0) + + } else { + # Compile a lookup table test. Lookup tables may be + # either compressed or fully resolved. If compressed + # (the resolution of the table is less than that of the + # range data, e.g., for floating point lookup tables) a + # LUT bin may have as its value, in addition to the + # usual 0 or 1, the address of an interpreter subprogram + # to be executed to test data values mapping to that bin. + # The subprogram pointed to may in turn be another lookup + # table, hence in the general case a tree of lookup tables + # and little code segments may be compiled to implement + # a complex range list test. + + # Get the data range of the lookup table. + xmin = XS(1) + if (IS_LEFTR(xmin)) + xmin = XE(1) + xmax = XE(nranges) + if (IS_RIGHTR(xmax)) + xmax = XS(nranges) + + # Get the lookup table size. Use a fully resolved table + # if the data is integer and the number of bins required + # is modest. + + nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex)) + + # Determine the mapping from data space to table space. + xoffset = xmin + xscale = nbins / (xmax - xmin) + + # Allocate and initialize the lookup table descriptor. + lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT) + call calloc (lut, nbins, TY_SHORT) + + LT_NEXT(lt) = EX_LTHEAD(ex) + EX_LTHEAD(ex) = lt + LT_TYPE(lt) = TY_REAL + LT_LUTP(lt) = lut + LT_NBINS(lt) = nbins + LT_R0(lt) = xoffset + LT_RS(lt) = xscale + LT_LEFT(lt) = btoi (IS_LEFTR(XS(1))) + LT_RIGHT(lt) = btoi (IS_RIGHTR(XE(nranges))) + + # Compile the LUTX test instruction. Save a back pointer + # to the instruction so that we can edit the jump field in + # case a subprogram is compiled after the LUTXt. + + lutx = qpex_pbpos (ex) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, LUTXS, offset, lt, 0) + else + call qpex_pbpin (ex, LUTXR, offset, lt, 0) + + xp = 1 + bin = 1 +continue_ + n_xs = xs + nranges + n_xe = xe + nranges + + # Initialize the lookup table. + do i = bin, nbins { + x1 = (i-1) / xscale + xoffset + x2 = i / xscale + xoffset + + # Get sub-rangelist for range x1:x2. + n_nranges = qpex_sublistr (x1, x2, + Memr[xs], Memr[xe], nranges, xp, + Memr[n_xs], Memr[n_xe]) + + if (n_nranges <= 0) { + Mems[lut+i-1] = 0 + + } else if (n_nranges == 1 && IS_LEFTR(Memr[n_xs]) && + IS_RIGHTR(Memr[n_xe])) { + + Mems[lut+i-1] = 1 + + } else { + # Compile the sub-rangelist as a subprogram. + + # First set the LUT bin to point to the subprogram. + # We cannot use the IP directly here since the LUT + # bins are short integer, so store the offset into + # the pb instead (guaranteed to be >= 4). + + Mems[lut+i-1] = qpex_pbpos(ex) - pb + + # Push a new context. + level = level + 1 + if (level > MAX_LEVELS) { + call eprintf ("%s: ") + call pargstr (qpexwarn) + call eprintf ("Excessive LUT nesting\n") + goto error_ + } + + # Save current LUT compilation context. + sv_xs[level] = xs + sv_xe[level] = xe + sv_xp[level] = xp + sv_xoffset[level] = xoffset + sv_xscale[level] = xscale + sv_nranges[level] = nranges + sv_lt[level] = lt + sv_bin[level] = i + sv_nbins[level] = nbins + sv_lut[level] = lut + sv_lutx[level] = lutx + + # Set up context for the new rangelist. + xs = n_xs + xe = n_xe + nranges = n_nranges + + goto next_ + } + } + + # Compile a test and exit instruction if the LUT calls any + # subprograms. + + if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION) + call qpex_pbpin (ex, XIFF, 0, 0, 0) + } +resume_ + # Resume lookup table compilation if exiting due to LUT-bin + # subprogram compilation. + + if (level > 0) { + # Pop saved context. + xs = sv_xs[level] + xe = sv_xe[level] + xp = sv_xp[level] + xoffset = sv_xoffset[level] + xscale = sv_xscale[level] + nranges = sv_nranges[level] + lt = sv_lt[level] + bin = sv_bin[level] + nbins = sv_nbins[level] + lut = sv_lut[level] + lutx = sv_lutx[level] + + # Compile a return from subprogram. + call qpex_pbpin (ex, RET, 0, 0, 0) + + # Patch up the original LUTX instruction to jump over the + # subprogram we have just finished compiling. + + IARG3(lutx) = qpex_pbpos (ex) + + # Resume compilation at the next LUT bin. + bin = bin + 1 + level = level - 1 + goto continue_ + } + } until (level <= 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION + + return (OK) +error_ + call qpex_free (ex, pb_save, db_save) + return (ERR) +end diff --git a/sys/qpoe/gen/qpexparsed.x b/sys/qpoe/gen/qpexparsed.x new file mode 100644 index 00000000..ec625bd8 --- /dev/null +++ b/sys/qpoe/gen/qpexparsed.x @@ -0,0 +1,372 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include <mach.h> +include "../qpex.h" + +.help qpexparse +.nf -------------------------------------------------------------------------- +QPEXPARSE -- Code to parse an event attribute expression, producing a binary +range list as output. + + nranges = qpex_parse[ird] (expr, xs, xe, xlen) + +The calling sequence for the parse routine is shown above. The arguments XS +and XE are pointers to dynamically allocated arrays of length XLEN and type +[IRD]. These arrays should be allocated in the calling program before calling +the parser, and deallocated when no longer needed. Reallocation to increase +the array length is automatic if the arrays fill during parsing. DTYPE should +be the same datatype as the attribute with which the list is associated. + +The form of an event attribute expression may be a list of values, + + attribute = n +or + attribute = m, n, ... + +a list of inclusive or exclusive ranges, + + attribute = m:n, !p:q + +including open ranges, + + attribute = :n, p:q + +or any combination of the above (excluding combinations of bitmasks and values +or ranges, which are mutually exclusive): + + attribute = :n, a, b, p:q, !(m, e:f) + +Parenthesis may be used for grouping where desired, e.g., + + attribute = (:n, a, b, p:q, !(m, e:f)) + +An additional form of the event attribute expression allows use of a bitmask +to specify the acceptable values, e.g., + + attribute = %17B +or + attribute = !%17B + +however, bitmasks are incompatible with range lists, and should be recognized +and dealt with elsewhere (bitmasks may not be combined with range lists in +the same expression term). + +We are concerned here only with the attribute value list itself, i.e., +everything to the right of the equals sign in the examples above. This list +should be extracted and placed into a string containing a single line of +text before we are called. Attribute value lists may be any length, but +backslash continuation, file inclusion (or whatever means is used to form +the attribute value list) is assumed to be handled at a higher level. + +The output of this package is an ordered boolean valued binary range list +with type integer, real, or double breakpoints (i.e., the breakpoints are the +same datatype as the attribute itself, but the range values are zero or one). +The range list defines the initial value, final value, and any interior +breakpoints where the attribute value changes state. Expression optimization +is used to minimize the number of breakpoints (i.e., eliminate redundant +breakpoints, such as a range within a range). + +Output range list format: + + xs[1] xe[1] + xs[2] xe[2] + ... + xs[N] xe[N] + +Where each range is inclusive and only "true" ranges are shown. If XS[1] is +LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right +range (n:) is indicated. In an integer range list, isolated points appear +as a single range with (xe[i]=xs[i]). In a real or double range list, +isolated points are represented as finite ranges with a width on the order of +the machine epsilon. +.endhelp --------------------------------------------------------------------- + +define DEF_XLEN 256 # default output range list length +define INC_XLEN 256 # increment to above +define DEF_VLEN 512 # default breakpoint list length +define INC_VLEN 512 # increment to above +define MAX_NEST 20 # parser stack depth + +define STEP 1 # step at boundary of closed range +define ZERO 1000 # step at boundary of open range + +define XV Memd[xv+($1)-1] # reference x position values +define UV Memi[uv+($1)-1] # unique flags for x value pairs +define SV Memi[sv+($1)-1] # reference breakpoint step values + + +# QPEX_PARSE -- Convert the given attribute value list into a binary +# range list, returning the number of ranges as the function value. + +int procedure qpex_parsed (expr, xs, xe, xlen) + +char expr[ARB] #I attribute value list to be parsed +pointer xs #U pointer to array of start-range values +pointer xe #U pointer to array of end-range values +int xlen #U allocated length of XS, XE arrays + +bool range +pointer xv, uv, sv +double xstart, xend, xmin, temp, x, n_xs, n_xe +int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy +int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin +errchk syserr, malloc, realloc +define pop_ 91 + +double dtemp +bool bval, fp_equald() +int qp_ctod() + +begin + vlen = DEF_VLEN + call malloc (xv, vlen, TY_DOUBLE) + call malloc (uv, vlen, TY_INT) + call malloc (sv, vlen, TY_INT) + + lev = 0 + nrg = 0 + + # Parse the expression string and compile the raw, unoptimized + # breakpoint list in the order in which the breakpoints occur in + # the string. + + for (ip=1; expr[ip] != EOS; ) { + # Skip whitespace. + for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip]) + ip = ip + 1 + + # Extract and process token. + switch (ch) { + case EOS: + # At end of string. + if (lev > 0) + goto pop_ + else + break + + case ',': + # Comma list token delmiter. + ip = ip + 1 + goto pop_ + + case '!', '(': + # Syntactical element - push on stack. + ip = ip + 1 + lev = lev + 1 + if (lev > MAX_NEST) + call syserr (SYS_QPEXLEVEL) + token[lev] = ch + tokval[lev] = nrg + 1 + + case ')': + # Close parenthesized group and pop parser stack. + ip = ip + 1 + if (lev < 1) + call syserr (SYS_QPEXMLP) + else if (token[lev] != '(') + call syserr (SYS_QPEXRPAREN) + lev = lev - 1 + goto pop_ + + default: + # Process a range term. + ip_start = ip + + # Scan the M in M:N. + if (qp_ctod (expr, ip, dtemp) <= 0) + xstart = LEFTD + else + xstart = dtemp + + # Scan the : in M:N. The notation M-N is also accepted, + # provided the token - immediately follows the token M. + + while (IS_WHITE(expr[ip])) + ip = ip + 1 + range = (expr[ip] == ':') + if (range) + ip = ip + 1 + else if (!IS_LEFTD (xstart)) { + range = (expr[ip] == '-') + if (range) + ip = ip + 1 + } + + # Scan the N in M:N. + if (range) { + if (qp_ctod (expr, ip, dtemp) <= 0) + xend = RIGHTD + else + xend = dtemp + } else + xend = xstart + + # Fix things if the user entered M:M explicitly. + if (range) + if (fp_equald (xstart, xend)) + range = false + + # Expand a single point into a range. For an integer list + # this produces M:M+1; for a floating list M-eps:M+eps. + # Verify ordering and that something recognizable was scanned. + + if (!range) { + if (IS_LEFTD(xstart)) + call syserr (SYS_QPEXBADRNG) + } else { + if (xstart > xend) { + temp = xstart; xstart = xend; xend = temp + } + } + + # Make more space if vectors fill up. + if (nrg+4 > vlen) { + vlen = vlen + INC_VLEN + call realloc (xv, vlen, TY_DOUBLE) + call realloc (uv, vlen, TY_INT) + call realloc (sv, vlen, TY_INT) + } + + # Save range on intermediate breakpoint list. + nrg = nrg + 1 + XV(nrg) = xstart + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = xend + UV(nrg) = 1 + SV(nrg) = -STEP +pop_ + # Pop parser stack. + if (lev > 0) + if (token[lev] == '!') { + # Invert a series of breakpoints. + do i = tokval[lev], nrg { + if (SV(i) == STEP) # invert + SV(i) = -ZERO + else if (SV(i) == -STEP) + SV(i) = ZERO + else if (SV(i) == ZERO) # undo + SV(i) = -STEP + else if (SV(i) == -ZERO) + SV(i) = STEP + } + lev = lev - 1 + } + } + } + + # If the first range entered by the user is an exclude range, + # e.g., "(!N)" or "(!(expr))" this implies that all other values + # are acceptable. Add the open range ":" to the end of the range + # list to indicate this, i.e., convert "!N" to ":,!N". + + if (SV(1) == -ZERO) { + nrg = nrg + 1 + XV(nrg) = LEFTD + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = RIGHTD + UV(nrg) = 1 + SV(nrg) = -STEP + } + + # Sort the breakpoint list. + do j = 1, nrg { + xmin = XV(j); umin = UV(j) + jval = j + do i = j+1, nrg { + bval = (XV(i) < xmin) + if (!bval) + if (abs (XV(i) - xmin) < 1.0E-5) + bval = (fp_equald(XV(i),xmin) && UV(i) < umin) + if (bval) { + xmin = XV(i); umin = UV(i) + jval = i + } + } + if (jval != j) { + temp = XV(j); XV(j) = XV(jval); XV(jval) = temp + itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp + itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp + } + } + + # Initialize the output arrays if they were passed in as null. + if (xlen <= 0) { + xlen = DEF_XLEN + call malloc (xs, xlen, TY_DOUBLE) + call malloc (xe, xlen, TY_DOUBLE) + } + + # Collapse sequences of redundant breakpoints into a single + # breakpoint, clipping the running sum value to the range 0-1. + # Accumulate and output successive nonzero ranges. + + op = 1 + ov = 0 + y = 0 + + for (r1=1; r1 <= nrg; r1=r2+1) { + # Get a range of breakpoint entries for a single XV position. + for (r2=r1; r2 <= nrg; r2=r2+1) { + bval = (UV(r2) != UV(r1)) + if (!bval) { + bval = (abs (XV(r2) - XV(r1)) > 1.0E-5) + if (!bval) + bval = !fp_equald(XV(r2),XV(r1)) + } + if (bval) + break + } + r2 = r2 - 1 + + # Collapse into a single breakpoint. + x = XV(r1) + dy = SV(r1) + do i = r1 + 1, r2 + dy = dy + SV(i) + y = y + dy + + # Clip value to the range 0-1. + v = max(0, min(1, y)) + + # Accumulate a range of nonzero value. This eliminates redundant + # points lying within a range which is already set high. + + if (v == 1 && ov == 0) { + n_xs = x + ov = 1 + } else if (v == 0 && ov == 1) { + n_xe = x + ov = 2 + } + + # Output a range. + if (ov == 2) { + if (op > xlen) { + xlen = xlen + INC_XLEN + call realloc (xs, xlen, TY_DOUBLE) + call realloc (xe, xlen, TY_DOUBLE) + } + + Memd[xs+op-1] = n_xs + Memd[xe+op-1] = n_xe + op = op + 1 + + ov = 0 + } + } + + # All done; discard breakpoint buffers. + call mfree (xv, TY_DOUBLE) + call mfree (uv, TY_INT) + call mfree (sv, TY_INT) + + return (op - 1) +end diff --git a/sys/qpoe/gen/qpexparsei.x b/sys/qpoe/gen/qpexparsei.x new file mode 100644 index 00000000..17d6a569 --- /dev/null +++ b/sys/qpoe/gen/qpexparsei.x @@ -0,0 +1,363 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include <mach.h> +include "../qpex.h" + +.help qpexparse +.nf -------------------------------------------------------------------------- +QPEXPARSE -- Code to parse an event attribute expression, producing a binary +range list as output. + + nranges = qpex_parse[ird] (expr, xs, xe, xlen) + +The calling sequence for the parse routine is shown above. The arguments XS +and XE are pointers to dynamically allocated arrays of length XLEN and type +[IRD]. These arrays should be allocated in the calling program before calling +the parser, and deallocated when no longer needed. Reallocation to increase +the array length is automatic if the arrays fill during parsing. DTYPE should +be the same datatype as the attribute with which the list is associated. + +The form of an event attribute expression may be a list of values, + + attribute = n +or + attribute = m, n, ... + +a list of inclusive or exclusive ranges, + + attribute = m:n, !p:q + +including open ranges, + + attribute = :n, p:q + +or any combination of the above (excluding combinations of bitmasks and values +or ranges, which are mutually exclusive): + + attribute = :n, a, b, p:q, !(m, e:f) + +Parenthesis may be used for grouping where desired, e.g., + + attribute = (:n, a, b, p:q, !(m, e:f)) + +An additional form of the event attribute expression allows use of a bitmask +to specify the acceptable values, e.g., + + attribute = %17B +or + attribute = !%17B + +however, bitmasks are incompatible with range lists, and should be recognized +and dealt with elsewhere (bitmasks may not be combined with range lists in +the same expression term). + +We are concerned here only with the attribute value list itself, i.e., +everything to the right of the equals sign in the examples above. This list +should be extracted and placed into a string containing a single line of +text before we are called. Attribute value lists may be any length, but +backslash continuation, file inclusion (or whatever means is used to form +the attribute value list) is assumed to be handled at a higher level. + +The output of this package is an ordered boolean valued binary range list +with type integer, real, or double breakpoints (i.e., the breakpoints are the +same datatype as the attribute itself, but the range values are zero or one). +The range list defines the initial value, final value, and any interior +breakpoints where the attribute value changes state. Expression optimization +is used to minimize the number of breakpoints (i.e., eliminate redundant +breakpoints, such as a range within a range). + +Output range list format: + + xs[1] xe[1] + xs[2] xe[2] + ... + xs[N] xe[N] + +Where each range is inclusive and only "true" ranges are shown. If XS[1] is +LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right +range (n:) is indicated. In an integer range list, isolated points appear +as a single range with (xe[i]=xs[i]). In a real or double range list, +isolated points are represented as finite ranges with a width on the order of +the machine epsilon. +.endhelp --------------------------------------------------------------------- + +define DEF_XLEN 256 # default output range list length +define INC_XLEN 256 # increment to above +define DEF_VLEN 512 # default breakpoint list length +define INC_VLEN 512 # increment to above +define MAX_NEST 20 # parser stack depth + +define STEP 1 # step at boundary of closed range +define ZERO 1000 # step at boundary of open range + +define XV Memi[xv+($1)-1] # reference x position values +define UV Memi[uv+($1)-1] # unique flags for x value pairs +define SV Memi[sv+($1)-1] # reference breakpoint step values + + +# QPEX_PARSE -- Convert the given attribute value list into a binary +# range list, returning the number of ranges as the function value. + +int procedure qpex_parsei (expr, xs, xe, xlen) + +char expr[ARB] #I attribute value list to be parsed +pointer xs #U pointer to array of start-range values +pointer xe #U pointer to array of end-range values +int xlen #U allocated length of XS, XE arrays + +bool range +pointer xv, uv, sv +int xstart, xend, xmin, temp, x, n_xs, n_xe +int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy +int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin +errchk syserr, malloc, realloc +define pop_ 91 + +int qp_ctoi() +define fp_equali($1==$2) + +begin + vlen = DEF_VLEN + call malloc (xv, vlen, TY_INT) + call malloc (uv, vlen, TY_INT) + call malloc (sv, vlen, TY_INT) + + lev = 0 + nrg = 0 + + # Parse the expression string and compile the raw, unoptimized + # breakpoint list in the order in which the breakpoints occur in + # the string. + + for (ip=1; expr[ip] != EOS; ) { + # Skip whitespace. + for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip]) + ip = ip + 1 + + # Extract and process token. + switch (ch) { + case EOS: + # At end of string. + if (lev > 0) + goto pop_ + else + break + + case ',': + # Comma list token delmiter. + ip = ip + 1 + goto pop_ + + case '!', '(': + # Syntactical element - push on stack. + ip = ip + 1 + lev = lev + 1 + if (lev > MAX_NEST) + call syserr (SYS_QPEXLEVEL) + token[lev] = ch + tokval[lev] = nrg + 1 + + case ')': + # Close parenthesized group and pop parser stack. + ip = ip + 1 + if (lev < 1) + call syserr (SYS_QPEXMLP) + else if (token[lev] != '(') + call syserr (SYS_QPEXRPAREN) + lev = lev - 1 + goto pop_ + + default: + # Process a range term. + ip_start = ip + + # Scan the M in M:N. + if (qp_ctoi (expr, ip, xstart) <= 0) + xstart = LEFTI + + # Scan the : in M:N. The notation M-N is also accepted, + # provided the token - immediately follows the token M. + + while (IS_WHITE(expr[ip])) + ip = ip + 1 + range = (expr[ip] == ':') + if (range) + ip = ip + 1 + else if (!IS_LEFTI (xstart)) { + range = (expr[ip] == '-') + if (range) + ip = ip + 1 + } + + # Scan the N in M:N. + if (range) { + if (qp_ctoi (expr, ip, xend) <= 0) + xend = RIGHTI + } else + xend = xstart + + # Fix things if the user entered M:M explicitly. + if (range) + if (fp_equali (xstart, xend)) + range = false + + # Expand a single point into a range. For an integer list + # this produces M:M+1; for a floating list M-eps:M+eps. + # Verify ordering and that something recognizable was scanned. + + if (!range) { + if (IS_LEFTI(xstart)) + call syserr (SYS_QPEXBADRNG) + xend = xstart + 1 + } else { + if (xstart > xend) { + temp = xstart; xstart = xend; xend = temp + } + if (!IS_RIGHTI(xend)) + xend = xend + 1 + } + + # Make more space if vectors fill up. + if (nrg+4 > vlen) { + vlen = vlen + INC_VLEN + call realloc (xv, vlen, TY_INT) + call realloc (uv, vlen, TY_INT) + call realloc (sv, vlen, TY_INT) + } + + # Save range on intermediate breakpoint list. + nrg = nrg + 1 + XV(nrg) = xstart + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = xend + UV(nrg) = 1 + SV(nrg) = -STEP +pop_ + # Pop parser stack. + if (lev > 0) + if (token[lev] == '!') { + # Invert a series of breakpoints. + do i = tokval[lev], nrg { + if (SV(i) == STEP) # invert + SV(i) = -ZERO + else if (SV(i) == -STEP) + SV(i) = ZERO + else if (SV(i) == ZERO) # undo + SV(i) = -STEP + else if (SV(i) == -ZERO) + SV(i) = STEP + } + lev = lev - 1 + } + } + } + + # If the first range entered by the user is an exclude range, + # e.g., "(!N)" or "(!(expr))" this implies that all other values + # are acceptable. Add the open range ":" to the end of the range + # list to indicate this, i.e., convert "!N" to ":,!N". + + if (SV(1) == -ZERO) { + nrg = nrg + 1 + XV(nrg) = LEFTI + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = RIGHTI + UV(nrg) = 1 + SV(nrg) = -STEP + } + + # Sort the breakpoint list. + do j = 1, nrg { + xmin = XV(j); umin = UV(j) + jval = j + do i = j+1, nrg { + if (XV(i) < xmin || (XV(i) == xmin && UV(i) < umin)) { + xmin = XV(i); umin = UV(i) + jval = i + } + } + if (jval != j) { + temp = XV(j); XV(j) = XV(jval); XV(jval) = temp + itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp + itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp + } + } + + # Initialize the output arrays if they were passed in as null. + if (xlen <= 0) { + xlen = DEF_XLEN + call malloc (xs, xlen, TY_INT) + call malloc (xe, xlen, TY_INT) + } + + # Collapse sequences of redundant breakpoints into a single + # breakpoint, clipping the running sum value to the range 0-1. + # Accumulate and output successive nonzero ranges. + + op = 1 + ov = 0 + y = 0 + + for (r1=1; r1 <= nrg; r1=r2+1) { + # Get a range of breakpoint entries for a single XV position. + for (r2=r1; r2 <= nrg; r2=r2+1) { + if (XV(r2) != XV(r1)) + break + } + r2 = r2 - 1 + + # Collapse into a single breakpoint. + x = XV(r1) + dy = SV(r1) + do i = r1 + 1, r2 + dy = dy + SV(i) + y = y + dy + + # Clip value to the range 0-1. + v = max(0, min(1, y)) + + # Accumulate a range of nonzero value. This eliminates redundant + # points lying within a range which is already set high. + + if (v == 1 && ov == 0) { + n_xs = x + ov = 1 + } else if (v == 0 && ov == 1) { + if (IS_RIGHTI(x)) + n_xe = x + else + n_xe = x - 1 + ov = 2 + } + + # Output a range. + if (ov == 2) { + if (op > xlen) { + xlen = xlen + INC_XLEN + call realloc (xs, xlen, TY_INT) + call realloc (xe, xlen, TY_INT) + } + + Memi[xs+op-1] = n_xs + Memi[xe+op-1] = n_xe + op = op + 1 + + ov = 0 + } + } + + # All done; discard breakpoint buffers. + call mfree (xv, TY_INT) + call mfree (uv, TY_INT) + call mfree (sv, TY_INT) + + return (op - 1) +end diff --git a/sys/qpoe/gen/qpexparser.x b/sys/qpoe/gen/qpexparser.x new file mode 100644 index 00000000..bf4c849e --- /dev/null +++ b/sys/qpoe/gen/qpexparser.x @@ -0,0 +1,372 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include <mach.h> +include "../qpex.h" + +.help qpexparse +.nf -------------------------------------------------------------------------- +QPEXPARSE -- Code to parse an event attribute expression, producing a binary +range list as output. + + nranges = qpex_parse[ird] (expr, xs, xe, xlen) + +The calling sequence for the parse routine is shown above. The arguments XS +and XE are pointers to dynamically allocated arrays of length XLEN and type +[IRD]. These arrays should be allocated in the calling program before calling +the parser, and deallocated when no longer needed. Reallocation to increase +the array length is automatic if the arrays fill during parsing. DTYPE should +be the same datatype as the attribute with which the list is associated. + +The form of an event attribute expression may be a list of values, + + attribute = n +or + attribute = m, n, ... + +a list of inclusive or exclusive ranges, + + attribute = m:n, !p:q + +including open ranges, + + attribute = :n, p:q + +or any combination of the above (excluding combinations of bitmasks and values +or ranges, which are mutually exclusive): + + attribute = :n, a, b, p:q, !(m, e:f) + +Parenthesis may be used for grouping where desired, e.g., + + attribute = (:n, a, b, p:q, !(m, e:f)) + +An additional form of the event attribute expression allows use of a bitmask +to specify the acceptable values, e.g., + + attribute = %17B +or + attribute = !%17B + +however, bitmasks are incompatible with range lists, and should be recognized +and dealt with elsewhere (bitmasks may not be combined with range lists in +the same expression term). + +We are concerned here only with the attribute value list itself, i.e., +everything to the right of the equals sign in the examples above. This list +should be extracted and placed into a string containing a single line of +text before we are called. Attribute value lists may be any length, but +backslash continuation, file inclusion (or whatever means is used to form +the attribute value list) is assumed to be handled at a higher level. + +The output of this package is an ordered boolean valued binary range list +with type integer, real, or double breakpoints (i.e., the breakpoints are the +same datatype as the attribute itself, but the range values are zero or one). +The range list defines the initial value, final value, and any interior +breakpoints where the attribute value changes state. Expression optimization +is used to minimize the number of breakpoints (i.e., eliminate redundant +breakpoints, such as a range within a range). + +Output range list format: + + xs[1] xe[1] + xs[2] xe[2] + ... + xs[N] xe[N] + +Where each range is inclusive and only "true" ranges are shown. If XS[1] is +LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right +range (n:) is indicated. In an integer range list, isolated points appear +as a single range with (xe[i]=xs[i]). In a real or double range list, +isolated points are represented as finite ranges with a width on the order of +the machine epsilon. +.endhelp --------------------------------------------------------------------- + +define DEF_XLEN 256 # default output range list length +define INC_XLEN 256 # increment to above +define DEF_VLEN 512 # default breakpoint list length +define INC_VLEN 512 # increment to above +define MAX_NEST 20 # parser stack depth + +define STEP 1 # step at boundary of closed range +define ZERO 1000 # step at boundary of open range + +define XV Memr[xv+($1)-1] # reference x position values +define UV Memi[uv+($1)-1] # unique flags for x value pairs +define SV Memi[sv+($1)-1] # reference breakpoint step values + + +# QPEX_PARSE -- Convert the given attribute value list into a binary +# range list, returning the number of ranges as the function value. + +int procedure qpex_parser (expr, xs, xe, xlen) + +char expr[ARB] #I attribute value list to be parsed +pointer xs #U pointer to array of start-range values +pointer xe #U pointer to array of end-range values +int xlen #U allocated length of XS, XE arrays + +bool range +pointer xv, uv, sv +real xstart, xend, xmin, temp, x, n_xs, n_xe +int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy +int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin +errchk syserr, malloc, realloc +define pop_ 91 + +double dtemp +bool bval, fp_equalr() +int qp_ctod() + +begin + vlen = DEF_VLEN + call malloc (xv, vlen, TY_REAL) + call malloc (uv, vlen, TY_INT) + call malloc (sv, vlen, TY_INT) + + lev = 0 + nrg = 0 + + # Parse the expression string and compile the raw, unoptimized + # breakpoint list in the order in which the breakpoints occur in + # the string. + + for (ip=1; expr[ip] != EOS; ) { + # Skip whitespace. + for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip]) + ip = ip + 1 + + # Extract and process token. + switch (ch) { + case EOS: + # At end of string. + if (lev > 0) + goto pop_ + else + break + + case ',': + # Comma list token delmiter. + ip = ip + 1 + goto pop_ + + case '!', '(': + # Syntactical element - push on stack. + ip = ip + 1 + lev = lev + 1 + if (lev > MAX_NEST) + call syserr (SYS_QPEXLEVEL) + token[lev] = ch + tokval[lev] = nrg + 1 + + case ')': + # Close parenthesized group and pop parser stack. + ip = ip + 1 + if (lev < 1) + call syserr (SYS_QPEXMLP) + else if (token[lev] != '(') + call syserr (SYS_QPEXRPAREN) + lev = lev - 1 + goto pop_ + + default: + # Process a range term. + ip_start = ip + + # Scan the M in M:N. + if (qp_ctod (expr, ip, dtemp) <= 0) + xstart = LEFTR + else + xstart = dtemp + + # Scan the : in M:N. The notation M-N is also accepted, + # provided the token - immediately follows the token M. + + while (IS_WHITE(expr[ip])) + ip = ip + 1 + range = (expr[ip] == ':') + if (range) + ip = ip + 1 + else if (!IS_LEFTR (xstart)) { + range = (expr[ip] == '-') + if (range) + ip = ip + 1 + } + + # Scan the N in M:N. + if (range) { + if (qp_ctod (expr, ip, dtemp) <= 0) + xend = RIGHTR + else + xend = dtemp + } else + xend = xstart + + # Fix things if the user entered M:M explicitly. + if (range) + if (fp_equalr (xstart, xend)) + range = false + + # Expand a single point into a range. For an integer list + # this produces M:M+1; for a floating list M-eps:M+eps. + # Verify ordering and that something recognizable was scanned. + + if (!range) { + if (IS_LEFTR(xstart)) + call syserr (SYS_QPEXBADRNG) + } else { + if (xstart > xend) { + temp = xstart; xstart = xend; xend = temp + } + } + + # Make more space if vectors fill up. + if (nrg+4 > vlen) { + vlen = vlen + INC_VLEN + call realloc (xv, vlen, TY_REAL) + call realloc (uv, vlen, TY_INT) + call realloc (sv, vlen, TY_INT) + } + + # Save range on intermediate breakpoint list. + nrg = nrg + 1 + XV(nrg) = xstart + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = xend + UV(nrg) = 1 + SV(nrg) = -STEP +pop_ + # Pop parser stack. + if (lev > 0) + if (token[lev] == '!') { + # Invert a series of breakpoints. + do i = tokval[lev], nrg { + if (SV(i) == STEP) # invert + SV(i) = -ZERO + else if (SV(i) == -STEP) + SV(i) = ZERO + else if (SV(i) == ZERO) # undo + SV(i) = -STEP + else if (SV(i) == -ZERO) + SV(i) = STEP + } + lev = lev - 1 + } + } + } + + # If the first range entered by the user is an exclude range, + # e.g., "(!N)" or "(!(expr))" this implies that all other values + # are acceptable. Add the open range ":" to the end of the range + # list to indicate this, i.e., convert "!N" to ":,!N". + + if (SV(1) == -ZERO) { + nrg = nrg + 1 + XV(nrg) = LEFTR + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = RIGHTR + UV(nrg) = 1 + SV(nrg) = -STEP + } + + # Sort the breakpoint list. + do j = 1, nrg { + xmin = XV(j); umin = UV(j) + jval = j + do i = j+1, nrg { + bval = (XV(i) < xmin) + if (!bval) + if (abs (XV(i) - xmin) < 1.0E-5) + bval = (fp_equalr(XV(i),xmin) && UV(i) < umin) + if (bval) { + xmin = XV(i); umin = UV(i) + jval = i + } + } + if (jval != j) { + temp = XV(j); XV(j) = XV(jval); XV(jval) = temp + itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp + itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp + } + } + + # Initialize the output arrays if they were passed in as null. + if (xlen <= 0) { + xlen = DEF_XLEN + call malloc (xs, xlen, TY_REAL) + call malloc (xe, xlen, TY_REAL) + } + + # Collapse sequences of redundant breakpoints into a single + # breakpoint, clipping the running sum value to the range 0-1. + # Accumulate and output successive nonzero ranges. + + op = 1 + ov = 0 + y = 0 + + for (r1=1; r1 <= nrg; r1=r2+1) { + # Get a range of breakpoint entries for a single XV position. + for (r2=r1; r2 <= nrg; r2=r2+1) { + bval = (UV(r2) != UV(r1)) + if (!bval) { + bval = (abs (XV(r2) - XV(r1)) > 1.0E-5) + if (!bval) + bval = !fp_equalr(XV(r2),XV(r1)) + } + if (bval) + break + } + r2 = r2 - 1 + + # Collapse into a single breakpoint. + x = XV(r1) + dy = SV(r1) + do i = r1 + 1, r2 + dy = dy + SV(i) + y = y + dy + + # Clip value to the range 0-1. + v = max(0, min(1, y)) + + # Accumulate a range of nonzero value. This eliminates redundant + # points lying within a range which is already set high. + + if (v == 1 && ov == 0) { + n_xs = x + ov = 1 + } else if (v == 0 && ov == 1) { + n_xe = x + ov = 2 + } + + # Output a range. + if (ov == 2) { + if (op > xlen) { + xlen = xlen + INC_XLEN + call realloc (xs, xlen, TY_REAL) + call realloc (xe, xlen, TY_REAL) + } + + Memr[xs+op-1] = n_xs + Memr[xe+op-1] = n_xe + op = op + 1 + + ov = 0 + } + } + + # All done; discard breakpoint buffers. + call mfree (xv, TY_REAL) + call mfree (uv, TY_INT) + call mfree (sv, TY_INT) + + return (op - 1) +end diff --git a/sys/qpoe/gen/qpexsubd.x b/sys/qpoe/gen/qpexsubd.x new file mode 100644 index 00000000..2fab50fd --- /dev/null +++ b/sys/qpoe/gen/qpexsubd.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a +# larger range list. The number of ranges extracted is returned as the +# function value. + +int procedure qpex_sublistd (x1, x2, xs,xe,nranges,ip, o_xs,o_xe) + +double x1, x2 #I range to be extracted +double xs[nranges],xe[nranges] #I input range list +int nranges #I nranges in input list +int ip #U start position in input list +double o_xs[ARB],o_xe[ARB] #O output sublist + +double tol +int op, i + +begin + tol = (EPSILOND * 10.0D0) + + # Determine the range containing or immediately following the + # start point of the range of interest. + + while (x1 < xs[ip] && ip > 1) + ip = ip - 1 + while (x1 >= xs[ip]) + if (x1 <= xe[ip] || ip >= nranges) + break + else + ip = ip + 1 + + # Check for an empty output range list. + if (xs[ip] > x2) + return (0) + + # At least one input range contributes something to the output region. + # Copy a portion of the input range list to the ouput range list. + + op = 1 + do i = ip, nranges { + if (xs[i] <= x1) + o_xs[op] = LEFTD - tol + else + o_xs[op] = xs[i] + + if ((xe[i] - x2) >= tol) { + o_xe[op] = RIGHTD + tol + op = op + 1 + break + } else + o_xe[op] = xe[i] + + op = op + 1 + if (xs[i+1] > x2) + break + } + + ip = i + return (op - 1) +end diff --git a/sys/qpoe/gen/qpexsubi.x b/sys/qpoe/gen/qpexsubi.x new file mode 100644 index 00000000..62ce5087 --- /dev/null +++ b/sys/qpoe/gen/qpexsubi.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a +# larger range list. The number of ranges extracted is returned as the +# function value. + +int procedure qpex_sublisti (x1, x2, xs,xe,nranges,ip, o_xs,o_xe) + +int x1, x2 #I range to be extracted +int xs[nranges],xe[nranges] #I input range list +int nranges #I nranges in input list +int ip #U start position in input list +int o_xs[ARB],o_xe[ARB] #O output sublist + +int tol +int op, i + +begin + tol = 0 + + # Determine the range containing or immediately following the + # start point of the range of interest. + + while (x1 < xs[ip] && ip > 1) + ip = ip - 1 + while (x1 >= xs[ip]) + if (x1 <= xe[ip] || ip >= nranges) + break + else + ip = ip + 1 + + # Check for an empty output range list. + if (xs[ip] > x2) + return (0) + + # At least one input range contributes something to the output region. + # Copy a portion of the input range list to the ouput range list. + + op = 1 + do i = ip, nranges { + if (xs[i] <= x1) + o_xs[op] = LEFTI - tol + else + o_xs[op] = xs[i] + + if ((xe[i] - x2) >= tol) { + o_xe[op] = RIGHTI + tol + op = op + 1 + break + } else + o_xe[op] = xe[i] + + op = op + 1 + if (xs[i+1] > x2) + break + } + + ip = i + return (op - 1) +end diff --git a/sys/qpoe/gen/qpexsubr.x b/sys/qpoe/gen/qpexsubr.x new file mode 100644 index 00000000..147bf14b --- /dev/null +++ b/sys/qpoe/gen/qpexsubr.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a +# larger range list. The number of ranges extracted is returned as the +# function value. + +int procedure qpex_sublistr (x1, x2, xs,xe,nranges,ip, o_xs,o_xe) + +real x1, x2 #I range to be extracted +real xs[nranges],xe[nranges] #I input range list +int nranges #I nranges in input list +int ip #U start position in input list +real o_xs[ARB],o_xe[ARB] #O output sublist + +real tol +int op, i + +begin + tol = (EPSILONR * 10.0) + + # Determine the range containing or immediately following the + # start point of the range of interest. + + while (x1 < xs[ip] && ip > 1) + ip = ip - 1 + while (x1 >= xs[ip]) + if (x1 <= xe[ip] || ip >= nranges) + break + else + ip = ip + 1 + + # Check for an empty output range list. + if (xs[ip] > x2) + return (0) + + # At least one input range contributes something to the output region. + # Copy a portion of the input range list to the ouput range list. + + op = 1 + do i = ip, nranges { + if (xs[i] <= x1) + o_xs[op] = LEFTR - tol + else + o_xs[op] = xs[i] + + if ((xe[i] - x2) >= tol) { + o_xe[op] = RIGHTR + tol + op = op + 1 + break + } else + o_xe[op] = xe[i] + + op = op + 1 + if (xs[i+1] > x2) + break + } + + ip = i + return (op - 1) +end diff --git a/sys/qpoe/gen/qpgetc.x b/sys/qpoe/gen/qpgetc.x new file mode 100644 index 00000000..1b6ce6fe --- /dev/null +++ b/sys/qpoe/gen/qpgetc.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +char procedure qp_getc (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +char value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (NULL) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_CHAR) + call pargc (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpgetd.x b/sys/qpoe/gen/qpgetd.x new file mode 100644 index 00000000..fea90d0f --- /dev/null +++ b/sys/qpoe/gen/qpgetd.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +double procedure qp_getd (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +double value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (INDEFD) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_DOUBLE) + call pargd (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpgeti.x b/sys/qpoe/gen/qpgeti.x new file mode 100644 index 00000000..c40d5de6 --- /dev/null +++ b/sys/qpoe/gen/qpgeti.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +int procedure qp_geti (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +int value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (INDEFI) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_INT) + call pargi (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpgetl.x b/sys/qpoe/gen/qpgetl.x new file mode 100644 index 00000000..804e2def --- /dev/null +++ b/sys/qpoe/gen/qpgetl.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +long procedure qp_getl (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +long value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (INDEFL) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_LONG) + call pargl (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpgetr.x b/sys/qpoe/gen/qpgetr.x new file mode 100644 index 00000000..1990a413 --- /dev/null +++ b/sys/qpoe/gen/qpgetr.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +real procedure qp_getr (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +real value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (INDEFR) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_REAL) + call pargr (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpgets.x b/sys/qpoe/gen/qpgets.x new file mode 100644 index 00000000..3f6500ef --- /dev/null +++ b/sys/qpoe/gen/qpgets.x @@ -0,0 +1,63 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +short procedure qp_gets (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +short value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + value = (INDEFS) + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_SHORT) + call pargs (value) + } + + return (value) +end diff --git a/sys/qpoe/gen/qpiogetev.x b/sys/qpoe/gen/qpiogetev.x new file mode 100644 index 00000000..7d029a94 --- /dev/null +++ b/sys/qpoe/gen/qpiogetev.x @@ -0,0 +1,1968 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <pmset.h> +include "../qpio.h" + +define RLI_NEXTLINE 9998 +define RLI_INITIALIZE 9999 +define SZ_CODE 7 + +# QPIO_GETEVENTS -- Return a sequence of events sharing the same mask value +# which satisfy the current event attribute filter. The returned events will +# be only those in a rectangular subregion of the image (specified by a prior +# call to qpio_setrange) which are also visible through the current mask. +# Sequences of events are returned in storage order until the region is +# exhausted, at which time EOF is returned. +# +# NOTE - If debug statements (printfs) are placed in this code they will cause +# i/o problems at runtime due to reentrancy, since this routine is called in +# a low level FIO pseudodevice driver (QPF). This is also true of any of the +# routines called by this procedure, and of the related routine QPIO_READPIX. + +int procedure qpio_gvtevents (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int status +char code[SZ_CODE] +int qpx_gvs(), qpx_gvi(), qpx_gvl(), qpx_gvr(), qpx_gvd() +errchk syserrs +define err_ 91 + +begin + # The generic routines currently require that X,Y be the same type. + # It wouldn't be hard to remove this restriction if necessary, but + # it simplifies things and I doubt if a mixed types feature would + # be used very often. + + if (IO_EVXTYPE(io) != IO_EVYTYPE(io)) + goto err_ + + # Get the events. + switch (IO_EVXTYPE(io)) { + case TY_SHORT: + status = qpx_gvs (io, o_ev, maskval, maxev, o_nev) + case TY_INT: + status = qpx_gvi (io, o_ev, maskval, maxev, o_nev) + case TY_LONG: + status = qpx_gvl (io, o_ev, maskval, maxev, o_nev) + case TY_REAL: + status = qpx_gvr (io, o_ev, maskval, maxev, o_nev) + case TY_DOUBLE: + status = qpx_gvd (io, o_ev, maskval, maxev, o_nev) + default: +err_ call sprintf (code, SZ_CODE, "%d") + call pargi (IO_EVXTYPE(io)) + call syserrs (SYS_QPINVEVT, code) + } + + return (status) +end + + + + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gvs (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_SHORT + 1 + xs = Mems[ev_p+ev_xoff] + ys = Mems[ev_p+ev_yoff] + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_SHORT + 1 + xe = Mems[ev_p+ev_xoff] + ye = Mems[ev_p+ev_yoff] + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_SHORT + 1 + + x = Mems[ev_p+ev_xoff] + y = Mems[ev_p+ev_yoff] + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_SHORT + 1 + x = Mems[ev_p+ev_xoff] + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + + + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gvi (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_INT + 1 + xs = Memi[ev_p+ev_xoff] + ys = Memi[ev_p+ev_yoff] + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_INT + 1 + xe = Memi[ev_p+ev_xoff] + ye = Memi[ev_p+ev_yoff] + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_INT + 1 + + x = Memi[ev_p+ev_xoff] + y = Memi[ev_p+ev_yoff] + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_INT + 1 + x = Memi[ev_p+ev_xoff] + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + + + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gvl (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_LONG + 1 + xs = Meml[ev_p+ev_xoff] + ys = Meml[ev_p+ev_yoff] + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_LONG + 1 + xe = Meml[ev_p+ev_xoff] + ye = Meml[ev_p+ev_yoff] + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_LONG + 1 + + x = Meml[ev_p+ev_xoff] + y = Meml[ev_p+ev_yoff] + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_LONG + 1 + x = Meml[ev_p+ev_xoff] + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + + + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gvr (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_REAL + 1 + xs = Memr[ev_p+ev_xoff] + 0.5 + ys = Memr[ev_p+ev_yoff] + 0.5 + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_REAL + 1 + xe = Memr[ev_p+ev_xoff] + 0.5 + ye = Memr[ev_p+ev_yoff] + 0.5 + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_REAL + 1 + + x = Memr[ev_p+ev_xoff] + 0.5 + y = Memr[ev_p+ev_yoff] + 0.5 + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_REAL + 1 + x = Memr[ev_p+ev_xoff] + 0.5 + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + + + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gvd (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_DOUBLE + 1 + xs = Memd[ev_p+ev_xoff] + 0.5 + ys = Memd[ev_p+ev_yoff] + 0.5 + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_DOUBLE + 1 + xe = Memd[ev_p+ev_xoff] + 0.5 + ye = Memd[ev_p+ev_yoff] + 0.5 + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1 + + x = Memd[ev_p+ev_xoff] + 0.5 + y = Memd[ev_p+ev_yoff] + 0.5 + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1 + x = Memd[ev_p+ev_xoff] + 0.5 + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + + diff --git a/sys/qpoe/gen/qpiorpixi.x b/sys/qpoe/gen/qpiorpixi.x new file mode 100644 index 00000000..c64f0a8a --- /dev/null +++ b/sys/qpoe/gen/qpiorpixi.x @@ -0,0 +1,150 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <syserr.h> +include "../qpio.h" + +# QPIO_READPIX -- Sample the event list within the indicated rectangular +# region, using the given blocking factor, to produce a rectangular array +# of "pixels", where each pixel is a count of the number of events mapping +# to that location which pass the event attribute filter and region mask. +# +# NOTE -- It is left up to the caller to zero the output buffer before +# we are called. (We merely increment the counts of the affected pixels). + +int procedure qpio_readpixi (io, obuf, vs, ve, ndim, xblock, yblock) + +pointer io #I QPIO descriptor +int obuf[ARB] #O output pixel buffer +int vs[ndim], ve[ndim] #I vectors defining region to be extracted +int ndim #I should be 2 for QPOE +real xblock, yblock #I blocking factors + +double x, y +pointer sp, evl, ev_p +int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j +errchk qpio_getevents, qpio_setrange, syserr +int qpio_getevents() + +begin + # Verify arguments. + if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1)) + call syserr (SYS_QPBLOCKOOR) + if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1)) + call syserr (SYS_QPBLOCKOOR) + + # Compute the size of the output matrix in integer pixels. This + # truncates the last partially filled pixel in each axis. + + xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000)) + yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000)) + if (xw <= 0 || yw <= 0) + return (0) + + call smark (sp) + call salloc (evl, SZ_EVLIST, TY_POINTER) + + xoff = IO_EVXOFF(io) + yoff = IO_EVYOFF(io) + maxpix = xw * yw + totev = 0 + + evtype = IO_EVXTYPE(io) + if (IO_EVXTYPE(io) != IO_EVYTYPE(io)) + call syserr (SYS_QPINVEVT) + + # Define the region from which we wish to read events. + call qpio_setrange (io, vs, ve, ndim) + + # Read the events. + while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) { + switch (evtype) { + + case TY_SHORT: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_SHORT + 1 + + x = Mems[ev_p+xoff] + y = Mems[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_INT: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_INT + 1 + + x = Memi[ev_p+xoff] + y = Memi[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_LONG: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_LONG + 1 + + x = Meml[ev_p+xoff] + y = Meml[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_REAL: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_REAL + 1 + + x = Memr[ev_p+xoff] + y = Memr[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_DOUBLE: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_DOUBLE + 1 + + x = Memd[ev_p+xoff] + y = Memd[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + } + + totev = totev + nev + } + + call sfree (sp) + return (totev) +end diff --git a/sys/qpoe/gen/qpiorpixs.x b/sys/qpoe/gen/qpiorpixs.x new file mode 100644 index 00000000..d97c7c42 --- /dev/null +++ b/sys/qpoe/gen/qpiorpixs.x @@ -0,0 +1,150 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <syserr.h> +include "../qpio.h" + +# QPIO_READPIX -- Sample the event list within the indicated rectangular +# region, using the given blocking factor, to produce a rectangular array +# of "pixels", where each pixel is a count of the number of events mapping +# to that location which pass the event attribute filter and region mask. +# +# NOTE -- It is left up to the caller to zero the output buffer before +# we are called. (We merely increment the counts of the affected pixels). + +int procedure qpio_readpixs (io, obuf, vs, ve, ndim, xblock, yblock) + +pointer io #I QPIO descriptor +short obuf[ARB] #O output pixel buffer +int vs[ndim], ve[ndim] #I vectors defining region to be extracted +int ndim #I should be 2 for QPOE +real xblock, yblock #I blocking factors + +double x, y +pointer sp, evl, ev_p +int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j +errchk qpio_getevents, qpio_setrange, syserr +int qpio_getevents() + +begin + # Verify arguments. + if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1)) + call syserr (SYS_QPBLOCKOOR) + if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1)) + call syserr (SYS_QPBLOCKOOR) + + # Compute the size of the output matrix in integer pixels. This + # truncates the last partially filled pixel in each axis. + + xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000)) + yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000)) + if (xw <= 0 || yw <= 0) + return (0) + + call smark (sp) + call salloc (evl, SZ_EVLIST, TY_POINTER) + + xoff = IO_EVXOFF(io) + yoff = IO_EVYOFF(io) + maxpix = xw * yw + totev = 0 + + evtype = IO_EVXTYPE(io) + if (IO_EVXTYPE(io) != IO_EVYTYPE(io)) + call syserr (SYS_QPINVEVT) + + # Define the region from which we wish to read events. + call qpio_setrange (io, vs, ve, ndim) + + # Read the events. + while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) { + switch (evtype) { + + case TY_SHORT: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_SHORT + 1 + + x = Mems[ev_p+xoff] + y = Mems[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_INT: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_INT + 1 + + x = Memi[ev_p+xoff] + y = Memi[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_LONG: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_LONG + 1 + + x = Meml[ev_p+xoff] + y = Meml[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_REAL: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_REAL + 1 + + x = Memr[ev_p+xoff] + y = Memr[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + case TY_DOUBLE: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_DOUBLE + 1 + + x = Memd[ev_p+xoff] + y = Memd[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + + } + + totev = totev + nev + } + + call sfree (sp) + return (totev) +end diff --git a/sys/qpoe/gen/qpputc.x b/sys/qpoe/gen/qpputc.x new file mode 100644 index 00000000..4415a177 --- /dev/null +++ b/sys/qpoe/gen/qpputc.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_putc (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_CHAR) + call pargi (dtype) + call pargc (value) + } + + indef = IS_INDEF(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qpputd.x b/sys/qpoe/gen/qpputd.x new file mode 100644 index 00000000..2c9883e0 --- /dev/null +++ b/sys/qpoe/gen/qpputd.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_putd (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +double value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_DOUBLE) + call pargi (dtype) + call pargd (value) + } + + indef = IS_INDEFD(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qpputi.x b/sys/qpoe/gen/qpputi.x new file mode 100644 index 00000000..528e6bc7 --- /dev/null +++ b/sys/qpoe/gen/qpputi.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_puti (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +int value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_INT) + call pargi (dtype) + call pargi (value) + } + + indef = IS_INDEFI(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qpputl.x b/sys/qpoe/gen/qpputl.x new file mode 100644 index 00000000..50e6605a --- /dev/null +++ b/sys/qpoe/gen/qpputl.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_putl (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +long value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_LONG) + call pargi (dtype) + call pargl (value) + } + + indef = IS_INDEFL(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qpputr.x b/sys/qpoe/gen/qpputr.x new file mode 100644 index 00000000..10af764b --- /dev/null +++ b/sys/qpoe/gen/qpputr.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_putr (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +real value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_REAL) + call pargi (dtype) + call pargr (value) + } + + indef = IS_INDEFR(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qpputs.x b/sys/qpoe/gen/qpputs.x new file mode 100644 index 00000000..ec58607a --- /dev/null +++ b/sys/qpoe/gen/qpputs.x @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_puts (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +short value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_SHORT) + call pargi (dtype) + call pargs (value) + } + + indef = IS_INDEFS(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/gen/qprlmerged.x b/sys/qpoe/gen/qprlmerged.x new file mode 100644 index 00000000..d08f4e5f --- /dev/null +++ b/sys/qpoe/gen/qprlmerged.x @@ -0,0 +1,134 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are +# common to both range lists are output. The number of ranges in the +# output range list is returned as the function value. + +int procedure qp_rlmerged (os,oe,olen, xs,xe,nx, ys,ye,ny) + +pointer os, oe #U output range list +int olen #U allocated length of OS, OE arrays + +double xs[ARB], xe[ARB] #I range list to be merged with +int nx #I number of ranges in X list +double ys[ARB], ye[ARB] #I range list to be merged with X +int ny #I number of ranges in Y list + +double o1, o2 +int nx_out, xi, yi, i +double qp_minvald(), qp_maxvald() +bool qp_lessthand() +errchk realloc + +begin + nx_out = 0 + if (nx <= 0 || ny <= 0) + return (0) + + xi = 1 + yi = 1 + + do i = 1, ARB { + # Find a pair of ranges which intersect. + repeat { + if (qp_lessthand (xe[xi], ys[yi])) { + if (xi >= nx) + return (nx_out) + else + xi = xi + 1 + } else if (qp_lessthand (ye[yi], xs[xi])) { + if (yi >= ny) + return (nx_out) + else + yi = yi + 1 + } else + break + } + + # Compute the intersection. + o1 = qp_maxvald (xs[xi], ys[yi]) + o2 = qp_minvald (xe[xi], ye[yi]) + + # Output the range. + if (nx_out + 1 > olen) { + olen = max (DEF_XLEN, olen * 2) + call realloc (os, olen, TY_DOUBLE) + call realloc (oe, olen, TY_DOUBLE) + } + + Memd[os+nx_out] = o1 + Memd[oe+nx_out] = o2 + nx_out = nx_out + 1 + + # Advance to the next range. + if (xi < nx && qp_lessthand (xe[xi], ye[yi])) + xi = xi + 1 + else if (yi < ny) + yi = yi + 1 + else + break + } + + return (nx_out) +end + + +# QP_MINVAL -- Return the lesser of two values, where either value can +# be an open range. + +double procedure qp_minvald (x, y) + +double x #I first value +double y #I second value + +bool qp_lessthand() + +begin + if (qp_lessthand (x, y)) + return (x) + else + return (y) +end + + +# QP_MAXVAL -- Return the greater of two values, where either value can +# be an open range. + +double procedure qp_maxvald (x, y) + +double x #I first value +double y #I second value + +bool qp_lessthand() + +begin + if (qp_lessthand (x, y)) + return (y) + else + return (x) +end + + +# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open +# range values. + +bool procedure qp_lessthand (x, y) + +double x #I first value +double y #I second value + +begin + if (IS_LEFTD(x)) + return (!IS_LEFTD(y)) + else if (IS_RIGHTD(x)) + return (false) + else if (IS_LEFTD(y)) + return (false) + else if (IS_RIGHTD(y)) + return (true) + else + return (x < y) +end diff --git a/sys/qpoe/gen/qprlmergei.x b/sys/qpoe/gen/qprlmergei.x new file mode 100644 index 00000000..f8476178 --- /dev/null +++ b/sys/qpoe/gen/qprlmergei.x @@ -0,0 +1,134 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are +# common to both range lists are output. The number of ranges in the +# output range list is returned as the function value. + +int procedure qp_rlmergei (os,oe,olen, xs,xe,nx, ys,ye,ny) + +pointer os, oe #U output range list +int olen #U allocated length of OS, OE arrays + +int xs[ARB], xe[ARB] #I range list to be merged with +int nx #I number of ranges in X list +int ys[ARB], ye[ARB] #I range list to be merged with X +int ny #I number of ranges in Y list + +int o1, o2 +int nx_out, xi, yi, i +int qp_minvali(), qp_maxvali() +bool qp_lessthani() +errchk realloc + +begin + nx_out = 0 + if (nx <= 0 || ny <= 0) + return (0) + + xi = 1 + yi = 1 + + do i = 1, ARB { + # Find a pair of ranges which intersect. + repeat { + if (qp_lessthani (xe[xi], ys[yi])) { + if (xi >= nx) + return (nx_out) + else + xi = xi + 1 + } else if (qp_lessthani (ye[yi], xs[xi])) { + if (yi >= ny) + return (nx_out) + else + yi = yi + 1 + } else + break + } + + # Compute the intersection. + o1 = qp_maxvali (xs[xi], ys[yi]) + o2 = qp_minvali (xe[xi], ye[yi]) + + # Output the range. + if (nx_out + 1 > olen) { + olen = max (DEF_XLEN, olen * 2) + call realloc (os, olen, TY_INT) + call realloc (oe, olen, TY_INT) + } + + Memi[os+nx_out] = o1 + Memi[oe+nx_out] = o2 + nx_out = nx_out + 1 + + # Advance to the next range. + if (xi < nx && qp_lessthani (xe[xi], ye[yi])) + xi = xi + 1 + else if (yi < ny) + yi = yi + 1 + else + break + } + + return (nx_out) +end + + +# QP_MINVAL -- Return the lesser of two values, where either value can +# be an open range. + +int procedure qp_minvali (x, y) + +int x #I first value +int y #I second value + +bool qp_lessthani() + +begin + if (qp_lessthani (x, y)) + return (x) + else + return (y) +end + + +# QP_MAXVAL -- Return the greater of two values, where either value can +# be an open range. + +int procedure qp_maxvali (x, y) + +int x #I first value +int y #I second value + +bool qp_lessthani() + +begin + if (qp_lessthani (x, y)) + return (y) + else + return (x) +end + + +# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open +# range values. + +bool procedure qp_lessthani (x, y) + +int x #I first value +int y #I second value + +begin + if (IS_LEFTI(x)) + return (!IS_LEFTI(y)) + else if (IS_RIGHTI(x)) + return (false) + else if (IS_LEFTI(y)) + return (false) + else if (IS_RIGHTI(y)) + return (true) + else + return (x < y) +end diff --git a/sys/qpoe/gen/qprlmerger.x b/sys/qpoe/gen/qprlmerger.x new file mode 100644 index 00000000..a776a5db --- /dev/null +++ b/sys/qpoe/gen/qprlmerger.x @@ -0,0 +1,134 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are +# common to both range lists are output. The number of ranges in the +# output range list is returned as the function value. + +int procedure qp_rlmerger (os,oe,olen, xs,xe,nx, ys,ye,ny) + +pointer os, oe #U output range list +int olen #U allocated length of OS, OE arrays + +real xs[ARB], xe[ARB] #I range list to be merged with +int nx #I number of ranges in X list +real ys[ARB], ye[ARB] #I range list to be merged with X +int ny #I number of ranges in Y list + +real o1, o2 +int nx_out, xi, yi, i +real qp_minvalr(), qp_maxvalr() +bool qp_lessthanr() +errchk realloc + +begin + nx_out = 0 + if (nx <= 0 || ny <= 0) + return (0) + + xi = 1 + yi = 1 + + do i = 1, ARB { + # Find a pair of ranges which intersect. + repeat { + if (qp_lessthanr (xe[xi], ys[yi])) { + if (xi >= nx) + return (nx_out) + else + xi = xi + 1 + } else if (qp_lessthanr (ye[yi], xs[xi])) { + if (yi >= ny) + return (nx_out) + else + yi = yi + 1 + } else + break + } + + # Compute the intersection. + o1 = qp_maxvalr (xs[xi], ys[yi]) + o2 = qp_minvalr (xe[xi], ye[yi]) + + # Output the range. + if (nx_out + 1 > olen) { + olen = max (DEF_XLEN, olen * 2) + call realloc (os, olen, TY_REAL) + call realloc (oe, olen, TY_REAL) + } + + Memr[os+nx_out] = o1 + Memr[oe+nx_out] = o2 + nx_out = nx_out + 1 + + # Advance to the next range. + if (xi < nx && qp_lessthanr (xe[xi], ye[yi])) + xi = xi + 1 + else if (yi < ny) + yi = yi + 1 + else + break + } + + return (nx_out) +end + + +# QP_MINVAL -- Return the lesser of two values, where either value can +# be an open range. + +real procedure qp_minvalr (x, y) + +real x #I first value +real y #I second value + +bool qp_lessthanr() + +begin + if (qp_lessthanr (x, y)) + return (x) + else + return (y) +end + + +# QP_MAXVAL -- Return the greater of two values, where either value can +# be an open range. + +real procedure qp_maxvalr (x, y) + +real x #I first value +real y #I second value + +bool qp_lessthanr() + +begin + if (qp_lessthanr (x, y)) + return (y) + else + return (x) +end + + +# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open +# range values. + +bool procedure qp_lessthanr (x, y) + +real x #I first value +real y #I second value + +begin + if (IS_LEFTR(x)) + return (!IS_LEFTR(y)) + else if (IS_RIGHTR(x)) + return (false) + else if (IS_LEFTR(y)) + return (false) + else if (IS_RIGHTR(y)) + return (true) + else + return (x < y) +end diff --git a/sys/qpoe/mkpkg b/sys/qpoe/mkpkg new file mode 100644 index 00000000..b7553b0f --- /dev/null +++ b/sys/qpoe/mkpkg @@ -0,0 +1,133 @@ +# Make the QPOE (position ordered event file) library. + +$checkout libex.a lib$ +$update libex.a +$checkin libex.a lib$ +$exit + +zzdebug: +zzdebug.e: + $checkout libex.a lib$ + $update libex.a + $checkin libex.a lib$ + + $omake zzdebug.x <error.h> <ctype.h> <qpset.h> <qpexset.h> "qpoe.h" + $link -z zzdebug.o + ; + +generic: + $set GFLAGS = "-k -t csilrd -p gen/" + $ifolder (gen/qpgeti.x, qpget.gx) $generic $(GFLAGS) qpget.gx $endif + $ifolder (gen/qpputi.x, qpput.gx) $generic $(GFLAGS) qpput.gx $endif + + $set GFLAGS = "-k -t bcsilrdx -p gen/" + $ifolder (gen/qpaddb.x, qpadd.gx) $generic $(GFLAGS) qpadd.gx $endif + + $set GFLAGS = "-k -t ird -p gen/" + $ifolder (gen/qpexattrli.x, qpexattrl.gx) + $generic $(GFLAGS) qpexattrl.gx $endif + $ifolder (gen/qpexcodei.x, qpexcode.gx) + $generic $(GFLAGS) qpexcode.gx $endif + $ifolder (gen/qpexparsei.x, qpexparse.gx) + $generic $(GFLAGS) qpexparse.gx $endif + $ifolder (gen/qpexsubi.x, qpexsub.gx) + $generic $(GFLAGS) qpexsub.gx $endif + + $ifolder (gen/qprlmergei.x, qprlmerge.gx) + $generic $(GFLAGS) qprlmerge.gx $endif + + $set GFLAGS = "-k -t si -p gen/" + $ifolder (gen/qpiorpixi.x, qpiorpix.gx) + $generic $(GFLAGS) qpiorpix.gx $endif + $ifolder (gen/qpiogetev.x, qpiogetev.gx) + $generic -k -o gen/qpiogetev.x qpiogetev.gx $endif + ; + +libex.a: + # Retranslate any recently modified generic sources. + $ifeq (hostid, unix) + $call generic + $endif + + @gen # Update datatype expanded files. + + qpaccess.x qpoe.h + qpaccessf.x qpoe.h + qpaddf.x qpoe.h <error.h> <qpset.h> + qpastr.x qpoe.h + qpbind.x qpoe.h <fmset.h> + qpclose.x qpoe.h + qpcopy.x qpoe.h + qpcopyf.x qpoe.h <qpset.h> + qpctod.x + qpctoi.x <lexnum.h> + qpdelete.x qpoe.h + qpdeletef.x qpoe.h + qpdsym.x qpoe.h + qpdtype.x qpoe.h <ctype.h> + qpelsize.x + qpexclose.x qpex.h <mach.h> + qpexdata.x qpex.h <mach.h> + qpexdebug.x qpex.h qpoe.h <mach.h> <qpexset.h> + qpexdel.x qpex.h <mach.h> + qpexeval.x qpex.h <mach.h> + qpexgetat.x qpex.h <mach.h> + qpexgetfil.x qpex.h <mach.h> + qpexmodfil.x qpex.h qpoe.h <mach.h> + qpexopen.x qpex.h qpoe.h <mach.h> + qpexpand.x qpoe.h + qpgetb.x qpoe.h + qpgettok.x qpoe.h <ctype.h> <error.h> <fset.h> + qpgetx.x qpoe.h + qpgmsym.x qpoe.h + qpgnfn.x qpoe.h + qpgpar.x qpoe.h <ctype.h> + qpgpsym.x qpoe.h + qpgstr.x qpoe.h + qpinherit.x qpoe.h <error.h> + qpioclose.x qpio.h + qpiogetfil.x qpio.h qpoe.h <mach.h> + qpiogetrg.x qpio.h + qpiolmask.x <plset.h> qpio.h qpoe.h + qpiolwcs.x qpio.h + qpiomkidx.x qpio.h qpoe.h <error.h> <fset.h> <mach.h> + qpioopen.x qpex.h qpio.h qpoe.h <error.h> <fset.h> <mach.h>\ + <plset.h> + qpioparse.x qpex.h qpio.h qpoe.h <ctype.h> <mach.h> + qpioputev.x qpio.h qpoe.h <mach.h> + qpiorb.x qpio.h + qpiosetfil.x qpex.h qpio.h + qpioseti.x qpio.h <plset.h> <qpioset.h> + qpiosetr.x qpio.h <qpioset.h> + qpiosetrg.x qpio.h + qpiostati.x qpio.h <qpioset.h> + qpiostatr.x qpio.h <qpioset.h> + qpiosync.x qpio.h qpoe.h <fset.h> <mach.h> + qpiowb.x qpio.h qpoe.h <fset.h> <mach.h> + qplenf.x qpoe.h + qploadwcs.x qpoe.h + qpmacro.x qpex.h qpoe.h <ctype.h> <error.h> <finfo.h> + qpmkfname.x qpoe.h + qpopen.x qpio.h qpoe.h <fmset.h> + qpparse.x <ctype.h> + qpparsefl.x qpex.h qpoe.h + qppclose.x <fset.h> + qppopen.x qpoe.h + qpppar.x qpoe.h <ctype.h> + qppstr.x qpoe.h + qpputb.x qpoe.h + qpputx.x qpoe.h + qpqueryf.x qpoe.h <qpset.h> + qpread.x qpoe.h + qprebuild.x qpoe.h + qprename.x qpoe.h + qprenamef.x qpoe.h + qpsavewcs.x qpoe.h + qpseti.x qpoe.h <qpset.h> + qpsetr.x qpoe.h <qpset.h> + qpsizeof.x qpoe.h + qpstati.x qpoe.h <qpset.h> + qpstatr.x qpoe.h <qpset.h> + qpsync.x qpoe.h + qpwrite.x qpoe.h + ; diff --git a/sys/qpoe/qpaccess.x b/sys/qpoe/qpaccess.x new file mode 100644 index 00000000..b8f5079f --- /dev/null +++ b/sys/qpoe/qpaccess.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_ACCESS -- Test if the named poefile exists and is accessible with the +# given mode (mode=0 merely tests for the existence of the poefile). + +int procedure qp_access (poefile, mode) + +char poefile[ARB] #I poefile name +int mode #I access mode + +int status +pointer sp, fname +int access() + +begin + call smark (sp) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + + call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME) + status = access (Memc[fname], mode, 0) + + call sfree (sp) + return (status) +end diff --git a/sys/qpoe/qpaccessf.x b/sys/qpoe/qpaccessf.x new file mode 100644 index 00000000..4b11d570 --- /dev/null +++ b/sys/qpoe/qpaccessf.x @@ -0,0 +1,24 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_ACCESSF -- Test whether the named field (header parameter) exists. +# Globally aliased parameters are recursively expanded and must resolve to +# a normal parameter reference. + +int procedure qp_accessf (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer qp_gpsym() +errchk qp_bind, qp_gpsym + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + if (qp_gpsym (qp, param) != NULL) + return (YES) + else + return (NO) +end diff --git a/sys/qpoe/qpadd.gx b/sys/qpoe/qpadd.gx new file mode 100644 index 00000000..83f240ae --- /dev/null +++ b/sys/qpoe/qpadd.gx @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "../qpoe.h" + +# QP_ADD -- Set the value of a parameter, creating the parameter if it does +# not already exist. This works for the most common case of simple scalar +# valued header parameters, although any parameter may be written into it it +# already exists. Additional control over the parameter attributes is possible +# if the parameter is explicitly created with qp_addf before being written into. + +procedure qp_add$t (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +PIXEL value #I parameter value +char comment[ARB] #I comment field, if creating parameter + +char datatype[1] +errchk qp_accessf, qp_addf +string dtypes SPPTYPES +int qp_accessf() + +begin + if (qp_accessf (qp, param) == NO) { + datatype[1] = dtypes[TY_PIXEL] + call qp_addf (qp, param, datatype, 1, comment, 0) + } + call qp_put$t (qp, param, value) +end diff --git a/sys/qpoe/qpaddf.x b/sys/qpoe/qpaddf.x new file mode 100644 index 00000000..e72c2cc1 --- /dev/null +++ b/sys/qpoe/qpaddf.x @@ -0,0 +1,173 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <error.h> +include <qpset.h> +include "qpoe.h" + +# QP_ADDF -- Add a new field (header parameter) to the datafile. It is an +# error if the parameter redefines an existing symbol. For variable array +# parameters the initial size is zero, and a new lfile is allocated for the +# parameter value. For static parameters storage is initialized to all zeros. + +procedure qp_addf (qp, param, datatype, maxelem, comment, flags) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char datatype[ARB] #I parameter data type +int maxelem #I allocated length of parameter +char comment[ARB] #I comment describing parameter +int flags #I parameter flags + +bool newtype +pointer sp, text, st, fm, sym, pval, dsym, dd +int fd, sz_elem, type, nchars, dtype, nfields, i + +long note() +pointer qp_gpsym(), stenter(), strefstab() +int stpstr(), qp_dtype(), qp_parsefl(), gstrcpy +int fm_nextlfile(), fm_getfd(), qp_elementsize(), fm_fopen() +errchk qp_bind, qp_gpsym, stenter, stpstr, fm_nextlfile, fm_fopen +errchk fm_getfd, note, write, syserrs +define fixed_ 91 + +begin + call smark (sp) + call salloc (text, SZ_TEXTBUF, TY_CHAR) + + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + st = QP_ST(qp) + fm = QP_FM(qp) + + # Resolve any macro references in the 'datatype' text. + # (Disabled - not sure this is a good idea here). + + # nchars = qp_expandtext (qp, datatype, Memc[text], SZ_TEXTBUF) + nchars = gstrcpy (datatype, Memc[text], SZ_TEXTBUF) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_addf: `%s' typ=`%s' nel=%d com=`%s' flg=%oB\n") + call pargstr (param) + call pargstr (Memc[text]) + call pargi (maxelem) + call pargstr (comment) + call pargi (flags) + } + + # Check for a redefinition. + sym = qp_gpsym (qp, param) + if (sym != NULL) + call syserrs (SYS_QPREDEF, param) + + # Add the symbol. + sym = stenter (st, param, LEN_SYMBOL) + + # Determine symbol type. + dtype = qp_dtype (qp, Memc[text], dsym) + newtype = (dtype == TY_USER && dsym == NULL) + sz_elem = qp_elementsize (qp, Memc[text], INSTANCEOF) + + S_DTYPE(sym) = dtype + S_SZELEM(sym) = 0 + if (dsym != NULL) + S_DSYM(sym) = dsym - strefstab(st,0) + else + S_DSYM(sym) = 0 + + # If defining a new user datatype (domain), SZELEM is the size of + # a structure element in chars, and MAXELEM is the length of the + # field list string, which becomes the value of the domain definition + # parameter. + + if (newtype) { + S_MAXELEM(sym) = nchars + call salloc (dd, LEN_DDDES, TY_STRUCT) + iferr (nfields = qp_parsefl (qp, Memc[text], dd)) + call erract (EA_WARN) + else + S_SZELEM(sym) = DD_STRUCTLEN(dd) * SZ_STRUCT + } else + S_MAXELEM(sym) = maxelem + + # If no flags are specified, set SF_INHERIT for fixed length params. + if (flags == 0 && S_MAXELEM(sym) > 0) + S_FLAGS(sym) = SF_INHERIT + else if (flags == QPF_NONE) + S_FLAGS(sym) = 0 + else + S_FLAGS(sym) = flags + + # Comments are stored in the symbol table and cannot be modified. + if (comment[1] != EOS) + S_COMMENT(sym) = stpstr (st, comment, 0) + else + S_COMMENT(sym) = NULL + + # Initialize data storage for the parameter. + if (S_MAXELEM(sym) == 0) { + # A variable length parameter; store in it's own lfile. The + # initial length is zero, hence initialization is not needed. + + S_NELEM(sym) = 0 + S_OFFSET(sym) = 1 + + # If we run out of lfiles, try to make do by allocating a fixed + # amount of static storage. + + iferr (S_LFILE(sym) = fm_nextlfile(fm)) { + S_MAXELEM(sym) = (QP_FMPAGESIZE(qp) + sz_elem-1) / sz_elem + call erract (EA_WARN) + goto fixed_ + } + + if (dtype == TY_CHAR) + type = TEXT_FILE + else + type = BINARY_FILE + + fd = fm_fopen (fm, S_LFILE(sym), NEW_FILE, type) + call close (fd) + + } else { + # A fixed length parameter; allocate and initialize storage in + # LF_STATICPARS. +fixed_ + fd = fm_getfd (fm, LF_STATICPARS, APPEND, 0) + + S_NELEM(sym) = 0 + S_OFFSET(sym) = note (fd) + S_LFILE(sym) = LF_STATICPARS + nchars = S_MAXELEM(sym) * sz_elem + + # The param value is the field list (datatype parameter) for a + # domain definition; otherwise we do not have a value yet, so we + # merely allocate the storage and initialize to zero. + + if (newtype) { + call write (fd, Memc[text], nchars) + S_NELEM(sym) = S_MAXELEM(sym) + } else { + call salloc (pval, nchars, TY_CHAR) + call aclrc (Memc[pval], nchars) + call write (fd, Memc[pval], nchars) + } + + call fm_retfd (fm, S_LFILE(sym)) + } + + if (QP_DEBUG(qp) > 2) { + # Dump symbol. + call eprintf ("%s: FLG=%oB TYP=%d DSY=%xX NEL=%d ") + call pargstr (param) + do i = 1, 4 + call pargi (Memi[sym+i-1]) + call eprintf ("MEL=%d SZE=%d COM=%xX LFN=%d OFF=%d\n") + do i = 5, 9 + call pargi (Memi[sym+i-1]) + } + + QP_MODIFIED(qp) = YES + call sfree (sp) +end diff --git a/sys/qpoe/qpastr.x b/sys/qpoe/qpastr.x new file mode 100644 index 00000000..4c856b10 --- /dev/null +++ b/sys/qpoe/qpastr.x @@ -0,0 +1,35 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_ASTR -- Set the value of a string parameter, creating the parameter if +# it does not already exist. This works for the common case of string +# parameters allocated a fixed amount of space at create time (any type of +# string parameter can be written into if it already exists). Additional +# control over the parameter attributes is possible if the parameter is +# explicitly created with qp_addf before being written into. + +procedure qp_astr (qp, param, value, comment) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char value[ARB] #I parameter value +char comment[ARB] #I comment field, if creating parameter + +int nchars +int qp_accessf(), strlen() +errchk qp_accessf, qp_addf + +begin + # By default we allocate a somewhat bigger storage area than needed + # to store the string, to permit updates of a similar length. If + # more control over the maximum string length is needed, QP_ADDF + # should be called explicitly. + + if (qp_accessf (qp, param) == NO) { + nchars = (strlen(value) + INC_STRLEN-1) / INC_STRLEN * INC_STRLEN + call qp_addf (qp, param, "c", nchars, comment, 0) + } + + call qp_pstr (qp, param, value) +end diff --git a/sys/qpoe/qpbind.x b/sys/qpoe/qpbind.x new file mode 100644 index 00000000..cd9c9bb6 --- /dev/null +++ b/sys/qpoe/qpbind.x @@ -0,0 +1,48 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <fmset.h> +include "qpoe.h" + +# QP_BIND -- Fix the create-time QPOE file parameters. This is called +# after the open, when the first datafile access occurs. + +procedure qp_bind (qp) + +pointer qp #I QPOE descriptor + +int fd +pointer fm +pointer stopen() +int fm_fopen() +errchk stopen, fm_fopen + +begin + if (QP_ACTIVE(qp) == NO) { + fm = QP_FM(qp) + + # Create the initial symbol table. + QP_ST(qp) = stopen (QPOE_TITLE, + QP_STINDEXLEN(qp), QP_STSTABLEN(qp), QP_STSBUFSIZE(qp)) + + # Fix the datafile parameters. + call fm_seti (fm, FM_PAGESIZE, QP_FMPAGESIZE(qp)) + call fm_seti (fm, FM_MAXLFILES, QP_FMMAXLFILES(qp)) + call fm_seti (fm, FM_MAXPTPAGES, QP_FMMAXPTPAGES(qp)) + call fm_seti (fm, FM_FCACHESIZE, QP_FMCACHESIZE(qp)) + + # Create the QPOE header and static storage lfiles. + fd = fm_fopen (fm, LF_QPOE, NEW_FILE, BINARY_FILE) + call close (fd) + fd = fm_fopen (fm, LF_STATICPARS, NEW_FILE, BINARY_FILE) + call close (fd) + + # Must flag descriptor as active here to prevent reentrant + # calls via the procedures called by qp_inherit. + + QP_ACTIVE(qp) = YES + + # Inherit selected data objects from parent if NEW_COPY. + if (QP_MODE(qp) == NEW_COPY) + call qp_inherit (qp, QP_OQP(qp), STDERR) + } +end diff --git a/sys/qpoe/qpclose.x b/sys/qpoe/qpclose.x new file mode 100644 index 00000000..eb537622 --- /dev/null +++ b/sys/qpoe/qpclose.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_CLOSE -- Close an open QPOE descriptor and file. + +procedure qp_close (qp) + +pointer qp #I QPOE descriptor + +begin + # An open/close should produce an empty poefile. + if (QP_ACTIVE(qp) == NO) { + QP_MODIFIED(qp) = YES + call qp_bind (qp) + } + + # Update the poefile on disk. + call qp_flushpar (qp) + call qp_sync (qp) + + # Shut everything down. + call stclose (QP_ST(qp)) + call fm_close (QP_FM(qp)) + call mfree (qp, TY_STRUCT) +end diff --git a/sys/qpoe/qpcopy.x b/sys/qpoe/qpcopy.x new file mode 100644 index 00000000..6d0a597d --- /dev/null +++ b/sys/qpoe/qpcopy.x @@ -0,0 +1,28 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_COPY -- Copy a poefile. The output version is "rebuilt" in the process, +# i.e., the datafile is not merely physically copied, but instead is rebuilt to +# reclaim unused storage and render the file structures logically contiguous. + +procedure qp_copy (o_poefile, n_poefile) + +char o_poefile[ARB] #I old poefile name +char n_poefile[ARB] #I new poefile name + +pointer sp +pointer o_fname, n_fname +string extn QPOE_EXTN + +begin + call smark (sp) + call salloc (o_fname, SZ_PATHNAME, TY_CHAR) + call salloc (n_fname, SZ_PATHNAME, TY_CHAR) + + call qp_mkfname (o_poefile, extn, Memc[o_fname], SZ_PATHNAME) + call qp_mkfname (n_poefile, extn, Memc[n_fname], SZ_PATHNAME) + call fm_copy (Memc[o_fname], Memc[n_fname]) + + call sfree (sp) +end diff --git a/sys/qpoe/qpcopyf.x b/sys/qpoe/qpcopyf.x new file mode 100644 index 00000000..ba47ad4f --- /dev/null +++ b/sys/qpoe/qpcopyf.x @@ -0,0 +1,48 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpset.h> +include "qpoe.h" + +define MAX_NELEM 8192 # copy unit (chunk) size + +# QP_COPYF -- Copy a field (parameter), either within a datafile, or from one +# datafile to another. + +procedure qp_copyf (o_qp, o_param, n_qp, n_param) + +pointer o_qp #I QPOE descriptor of old (input) datafile +char o_param[ARB] #I input parameter name +pointer n_qp #I QPOE descriptor of new (output) datafile +char n_param[ARB] #I output parameter name + +pointer sp, dp, cp, buf +int nelem, elsize, chunk, nleft, first, maxelem, flags +int qp_queryf(), qp_accessf(), qp_elementsize(), qp_read() +errchk qp_queryf, qp_addf, qp_read, qp_write + +begin + call smark (sp) + call salloc (dp, SZ_DATATYPE, TY_CHAR) + call salloc (cp, SZ_COMMENT, TY_CHAR) + + # Get parameter attributes and create new parameter if necessary. + nelem = qp_queryf (o_qp, o_param, Memc[dp], maxelem, Memc[cp], flags) + if (qp_accessf (n_qp, n_param) == NO) + call qp_addf (n_qp, n_param, Memc[dp], maxelem, Memc[cp], flags) + + # Copy parameter value. + if (nelem > 0) { + elsize = qp_elementsize (o_qp, Memc[dp], INSTANCEOF) + chunk = min (MAX_NELEM, nelem) + call salloc (buf, chunk * elsize, TY_CHAR) + + first = 1 + for (nleft=nelem; nleft > 0; nleft=nleft-nelem) { + nelem = qp_read (o_qp,o_param, Memc[buf], chunk,first, Memc[dp]) + call qp_write (n_qp,n_param, Memc[buf], nelem,first, Memc[dp]) + first = first + nelem + } + } + + call sfree (sp) +end diff --git a/sys/qpoe/qpctod.x b/sys/qpoe/qpctod.x new file mode 100644 index 00000000..6487650c --- /dev/null +++ b/sys/qpoe/qpctod.x @@ -0,0 +1,34 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +define SZ_NUMBUF 32 # buffer for extracting numbers + +# QP_CTOD -- Return as a double the next numeric token from the input stream. +# This differs from the standard FMTIO procedures only in that colon is not +# considered a numeric character (as used in sexagesimal numbers). + +int procedure qp_ctod (str, ip, dval) + +char str[ARB] #I input string +int ip #U pointer into input string +double dval #O double value + +int nchars, op, i +char numbuf[SZ_NUMBUF] +int ctod() + +begin + i = ip + do op = 1, SZ_NUMBUF + if (str[i] != ':' && str[i] != EOS) { + numbuf[op] = str[i] + i = i + 1 + } else + break + + i = 1 + numbuf[op] = EOS + nchars = ctod (numbuf, i, dval) + ip = ip + i - 1 + + return (nchars) +end diff --git a/sys/qpoe/qpctoi.x b/sys/qpoe/qpctoi.x new file mode 100644 index 00000000..9ac70ddb --- /dev/null +++ b/sys/qpoe/qpctoi.x @@ -0,0 +1,34 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <lexnum.h> + +# QP_CTOI -- Decode an integer token from the input string, advancing the +# input pointer the the first character following the decoded number, and +# returning the number of numeric characters decoded as the function value. +# This is equivalent to the standard CTOI except that it calls LEXNUM first +# to determine the radix of the input number, hence can deals with hex and +# octal numbers as well as decimal. + +int procedure qp_ctoi (str, ip, ival) + +char str[ARB] #I input string +int ip #U pointer into input string +int ival #O integer value + +int ip_save, base, nchars +int gctol(), lexnum() + +begin + ip_save = ip + switch (lexnum (str, ip, nchars)) { + case LEX_OCTAL: + base = 8 + case LEX_HEX: + base = 16 + default: + base = 10 + } + + ip = ip_save + return (gctol (str, ip, ival, base)) +end diff --git a/sys/qpoe/qpdelete.x b/sys/qpoe/qpdelete.x new file mode 100644 index 00000000..16b32161 --- /dev/null +++ b/sys/qpoe/qpdelete.x @@ -0,0 +1,20 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_DELETE -- Delete a poefile. + +procedure qp_delete (poefile) + +char poefile[ARB] #I poefile name +pointer sp, fname + +begin + call smark (sp) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + + call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME) + call delete (Memc[fname]) + + call sfree (sp) +end diff --git a/sys/qpoe/qpdeletef.x b/sys/qpoe/qpdeletef.x new file mode 100644 index 00000000..b7c41189 --- /dev/null +++ b/sys/qpoe/qpdeletef.x @@ -0,0 +1,35 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_DELETEF -- Delete a header parameter. It is an error if the named header +# parameter does not exist. Deletions are permanent once the datafile is +# updated. + +procedure qp_deletef (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer sym +pointer qp_gpsym() +errchk qp_gpsym, syserrs + +begin + # Access the named parameter. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + else if (and (S_FLAGS(sym), SF_DELETED) != 0) + return + + # If the parameter value is stored in its own lfile, delete it. + if (S_LFILE(sym) > LF_STATICPARS) + call fm_lfdelete (QP_FM(qp), S_LFILE(sym)) + + # Set the delete bit in the symbol descriptor. + S_FLAGS(sym) = or (S_FLAGS(sym), SF_DELETED) + + QP_MODIFIED(qp) = YES +end diff --git a/sys/qpoe/qpdsym.x b/sys/qpoe/qpdsym.x new file mode 100644 index 00000000..d94b54b4 --- /dev/null +++ b/sys/qpoe/qpdsym.x @@ -0,0 +1,56 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_DSYM -- Dump the symbol table of a QPOE file. + +procedure qp_dsym (qp, out) + +pointer qp #I QPOE descriptor +int out #I output file + +int nsyms, i +pointer sp, st, sym, op, pname, syms +pointer sthead(), stnext(), stname() + +begin + call smark (sp) + st = QP_ST(qp) + + # Count the symbols. + nsyms = 0 + for (sym=sthead(st); sym != NULL; sym=stnext(st,sym)) + nsyms = nsyms + 1 + + # Construct a reversed array of symbol pointers. + call salloc (syms, nsyms, TY_POINTER) + op = syms + nsyms - 1 + for (sym=sthead(st); sym != NULL; sym=stnext(st,sym)) { + Memi[op] = sym + op = op - 1 + } + + # Output the symbols. + if (nsyms > 0) + call fprintf (out, +" SYMBOL FLAGS DTYPE DSYM NELEM MAXELEM SZELEM COMMENT LFILE OFFSET\n") + + do i = 1, nsyms { + sym = Memi[syms+i-1] + pname = stname (st, sym) + + call fprintf (out, "%16s %5o %5d %4d %5d %7d %6d %7x %5d %6d\n") + call pargstr (Memc[pname]) + call pargi (and (S_FLAGS(sym), 77777B)) + call pargi (S_DTYPE(sym)) + call pargi (S_DSYM(sym)) + call pargi (S_NELEM(sym)) + call pargi (S_MAXELEM(sym)) + call pargi (S_SZELEM(sym)) + call pargi (S_COMMENT(sym)) + call pargi (S_LFILE(sym)) + call pargi (S_OFFSET(sym)) + } + + call sfree (sp) +end diff --git a/sys/qpoe/qpdtype.x b/sys/qpoe/qpdtype.x new file mode 100644 index 00000000..ed5b9e89 --- /dev/null +++ b/sys/qpoe/qpdtype.x @@ -0,0 +1,57 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <ctype.h> +include "qpoe.h" + +# QP_DTYPE -- Translate the given symbolic datatype name into an integer +# type code. The possible type codes are most of the standard SPP TY_type +# codes, plus TY_MACRO, TY_OPAQUE, and TY_USER. If the symbol type is TY_USER +# (a user defined data structure or domain)), then a pointer to the symbol +# table entry for the named domain is returned as an output argument. The +# integer type code is returned as the function value. + +int procedure qp_dtype (qp, datatype, dsym) + +pointer qp #I QPOE descriptor +char datatype[ARB] #I symbolic datatype name +pointer dsym #O pointer to domain symbol, if TY_USER + +char junk[1] +int dtype, ip +string types "|bool|char|short|int|long|real|double|complex|macro|opaque|" + +pointer stfind() +int stridx(), strdic() + +begin + dtype = NULL + dsym = NULL + for (ip=1; IS_WHITE(datatype[ip]); ip=ip+1) + ; + + # Single character standard dtype code (bcsilrdx)? + if (datatype[ip+1] == EOS) + dtype = stridx (datatype[ip], SPPTYPES) + + # Spelled out dtype name. Check standard names first. + if (dtype == NULL) { + dtype = strdic (datatype[ip], junk, 1, types) + if (dtype == 9) + dtype = TY_MACRO + else if (dtype == 10) + dtype = TY_OPAQUE + } + + # Lastly, check the special types. + if (dtype == 0) { + if (datatype[ip] == '{') # field list + dtype = TY_USER + else { + dsym = stfind (QP_ST(qp), datatype[ip]) + if (dsym != NULL) + dtype = S_DTYPE(dsym) + } + } + + return (dtype) +end diff --git a/sys/qpoe/qpelsize.x b/sys/qpoe/qpelsize.x new file mode 100644 index 00000000..b84e0dfa --- /dev/null +++ b/sys/qpoe/qpelsize.x @@ -0,0 +1,20 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +# QP_ELEMENTSIZE -- Determine the size in chars of a QPOE datatype. This may +# be one of the special datatypes (user defined record types), or a primitive +# type. + +int procedure qp_elementsize (qp, datatype, reftype) + +pointer qp #I QPOE descriptor +char datatype[ARB] #I symbolic datatype name +int reftype #I type of reference (immediate or instanceof) + +pointer dsym +int dtype +int qp_sizeof(), qp_dtype() + +begin + dtype = qp_dtype (qp, datatype, dsym) + return (qp_sizeof (qp, dtype, dsym, reftype)) +end diff --git a/sys/qpoe/qpex.h b/sys/qpoe/qpex.h new file mode 100644 index 00000000..45cc3db0 --- /dev/null +++ b/sys/qpoe/qpex.h @@ -0,0 +1,164 @@ +# QPEX.H -- QPOE expression evaluator definitions. + +# Size limiting definitions. +define DEF_PROGBUFLEN 65536 # default program buffer length, ints +define DEF_DATABUFLEN 65536 # default data buffer length, chars +define DEF_SZEXPRBUF 2048 # default size expression buffer +define INC_SZEXPRBUF 2048 # increment if overflow +define DEF_XLEN 256 # default (initial) range buffer size +define MAX_INSTRUCTIONS ARB # arbitrary do-loop index +define MAX_LEVELS 32 # max levels of program nesting +define DEF_MAXRRLUTLEN 1024 # max RRLUT (reduced resolution) length +define DEF_MAXFRLUTLEN 8192 # max FRLUT (full resolution) length +define DEF_LUTMINRANGES 5 # use RRLUT if more ranges than this +define DEF_LUTSCALE 15 # multiplied by nranges to get rrlutlen + +# Magic values used to represent open ranges :N and N:. +define LEFTI -MAX_INT +define RIGHTI MAX_INT +define LEFTR -MAX_REAL +define RIGHTR MAX_REAL +define LEFTD -MAX_DOUBLE +define RIGHTD MAX_DOUBLE + +define IS_LEFTI (($1) == -MAX_INT) +define IS_RIGHTI (($1) == MAX_INT) +define IS_LEFTR (($1) <= -MAX_REAL) +define IS_RIGHTR (($1) >= MAX_REAL) +define IS_LEFTD (($1) <= -MAX_DOUBLE) +define IS_RIGHTD (($1) >= MAX_DOUBLE) + +# The compiled expression descriptor. The program buffer holds the compiled +# expression to be interpreted to test each data event structure. The data +# buffer is used to store program data, e.g., lookup table descriptors, +# TY_DOUBLE constants (these are too large to be stored directly in the +# compiled program), and the textual expressions compiled to generate the +# program; the latter are used by QPEX_GETFILTER to regenerate the current +# expression. The expression terms (ET) and lookup table (LT) descriptors +# are maintained on linked lists. New ET descriptors are linked onto the +# tail of the ET list; LT descriptors are linked onto the head of the LT list. +# The program and data buffers are *nonrelocatable* (hence fixed in size) +# to allow use of absolute pointers to reference structures within the buffers. + +define LEN_EXDES 16 +define EX_QP Memi[$1] # back pointer to QPOE descriptor +define EX_DEBUG Memi[$1+1] # debug level +define EX_START Memi[$1+2] # pointer to first instruction +define EX_PB Memi[$1+3] # pointer to program buffer (int) +define EX_PBTOP Memi[$1+4] # pointer to top+1 of pb +define EX_PBOP Memi[$1+5] # pointer to next avail. cell in pb +define EX_DB Memi[$1+6] # data buffer pointer (char) +define EX_DBTOP Memi[$1+7] # pointer to top+1 of db +define EX_DBOP Memi[$1+8] # pointer to next avail. cell in db +define EX_MAXFRLUTLEN Memi[$1+9] # max full-res lut length +define EX_MAXRRLUTLEN Memi[$1+10] # max reduced-res lut length +define EX_LUTMINRANGES Memi[$1+11] # min ranges required for a LUT +define EX_LUTSCALE Memi[$1+12] # scale nranges to frlutlen +define EX_ETHEAD Memi[$1+13] # offset of first expr term descriptor +define EX_ETTAIL Memi[$1+14] # offset of last expr term descriptor +define EX_LTHEAD Memi[$1+15] # offset of first LUT descriptor + +# Expression terms descriptor. Stored in the data buffer and maintained +# as a linked list. + +define LEN_ETDES 9 +define ET_ATTTYPE Memi[$1] # datatype of attribute +define ET_ATTOFF Memi[$1+1] # *typed* offset of attribute +define ET_PROGPTR Memi[$1+2] # pointer to program segment +define ET_NINSTR Memi[$1+3] # program segment size, instructions +define ET_DELETED Memi[$1+4] # set if term is deleted +define ET_ATNAME Memi[$1+5] # attribute name used in expr +define ET_ASSIGNOP Memi[$1+6] # type of assignment ("=", "+=") +define ET_EXPRTEXT Memi[$1+7] # saved expr text +define ET_NEXT Memi[$1+8] # databuf offset of next ET struct + +# Lookup table descriptor. Stored in the data buffer and maintained as a +# linked list. The table itself is separately allocated. + +define LEN_LTDES 10 +define LT_NEXT Memi[$1] # pointer to next stored LUT +define LT_TYPE Memi[$1+1] # TY_SHORT pointer to stored LUT +define LT_LUTP Memi[$1+2] # TY_SHORT pointer to stored LUT +define LT_NBINS Memi[$1+3] # number of lookup table entries +define LT_LEFT Memi[$1+4] # lut value if index off left end +define LT_RIGHT Memi[$1+5] # lut value if index off right end +define LT_I0 Memr[P2R($1+6)] # zero point for integer LUT +define LT_IS Memr[P2R($1+8)] # scale factor for integer LUT +define LT_R0 Memr[P2R($1+6)] # zero point for real LUT +define LT_RS Memr[P2R($1+8)] # scale factor for real LUT +define LT_D0 Memd[P2D($1+6)] # zero point for double LUT +define LT_DS Memd[P2D($1+8)] # scale factor for double LUT + +define LT_LUT Mems[LT_LUTP($1)+$2-1] # LT_LUT(lt,i) + +# Macros for referencing the fields of an instruction. TY_DOUBLE arguments +# are stored in the data buffer, storing an offset in the instruction field. + +define LEN_INSTRUCTION 4 # instruction length, ints + +define OPCODE Memi[$1] # instruction opcode. +define IARG1 Memi[$1+1] # first integer argument +define IARG2 Memi[$1+2] # second integer argument +define IARG3 Memi[$1+3] # third integer argument +define RARG1 Memr[P2R($1+1)] # first real argument +define RARG2 Memr[P2R($1+2)] # second real argument +define RARG3 Memr[P2R($1+3)] # third real argument +define DARG1 Memd[IARG1($1)] # first double argument +define DARG2 Memd[IARG2($1)] # second double argument +define DARG3 Memd[IARG3($1)] # third double argument + +# Instruction opcodes. + +define PASS 00 # set pass=true and return +define RET 01 # return from subprogram +define NOP 02 # no-operation +define GOTO 03 # goto offset +define XIFT 04 # exit if expr-value true +define XIFF 05 # exit if expr-value false +define LDSI 06 # load short to int +define LDII 07 # load int +define LDRR 08 # load real +define LDRD 09 # load real to double +define LDDD 10 # load double + +define BTTI 11 # bit test, int +define EQLI 12 # test if equal +define EQLR 13 +define EQLD 14 +define LEQI 15 # test if less than or equal +define LEQR 16 +define LEQD 17 +define GEQI 18 # test if greater than or equal +define GEQR 19 +define GEQD 20 +define RNGI 21 # range test +define RNGR 22 +define RNGD 23 + +define BTTXS 24 # bit test direct and exit if false +define BTTXI 25 +define NEQXS 26 # not equals test and exit +define NEQXI 27 +define NEQXR 28 +define NEQXD 29 +define EQLXS 30 # equality test direct and exit if false +define EQLXI 31 +define EQLXR 32 +define EQLXD 33 +define LEQXS 34 # LEQ test direct and exit if false +define LEQXI 35 +define LEQXR 36 +define LEQXD 37 +define GEQXS 38 # GEQ test direct and exit if false +define GEQXI 39 +define GEQXR 40 +define GEQXD 41 +define RNGXS 42 # range test direct and exit if false +define RNGXI 43 +define RNGXR 44 +define RNGXD 45 + +define LUTXS 46 # lookup table test +define LUTXI 47 +define LUTXR 48 +define LUTXD 49 diff --git a/sys/qpoe/qpexattrl.gx b/sys/qpoe/qpexattrl.gx new file mode 100644 index 00000000..7cd6cd0a --- /dev/null +++ b/sys/qpoe/qpexattrl.gx @@ -0,0 +1,127 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <ctype.h> +include "../qpex.h" + +# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a +# binary range list of the indicated type. This range list is a simplified +# version of the original filter expression, which may have contained +# multiple fields, some negated or overlapping, in any order, subsequently +# modified or deleted with qpex_modfilter, etc. The final resultant range +# list is ordered and consists of discreet nonoverlapping ranges. +# +# Upon input the variables XS and XE should either point to a pair of +# preallocated buffers of length XLEN, or they should be set to NULL. +# The routine will reallocate the buffers as necessary to allow for long +# range lists, updating XLEN so that it always contains the actual length +# of the arrays (which may not be completely full). Each list element +# consists of a pair of values (xs[i],xe[i]) defining the start and end +# points of the range. If xs[1] is INDEF the range is open to the left, +# if xe[nranges] is INDEF the range is open to the right. The number of +# ranges output is returned as the function value. + +int procedure qpex_attrl$t (ex, attribute, xs, xe, xlen) + +pointer ex #I QPEX descriptor +char attribute[ARB] #I attribute name +pointer xs #U pointer to array of start values +pointer xe #U pointer to array of end values +int xlen #U length of xs/xe arrays + +pointer ps, pe, qs, qe +pointer sp, expr, ip, ep +int plen, qlen, np, nq, nx +int neterms, nchars, maxch +int qpex_getattribute(), qpex_parse$t(), qp_rlmerge$t() + +begin + call smark (sp) + + # Get attribute filter expression. In the unlikely event that the + # expression is too large to fit in our buffer, repeat with a buffer + # twice as large until it fits. + + maxch = DEF_SZEXPRBUF + nchars = 0 + + repeat { + maxch = maxch * 2 + call salloc (expr, maxch, TY_CHAR) + nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch) + if (nchars <= 0) + break + } until (nchars < maxch) + + # Parse expression to produce a range list. If the expression + # contains multiple eterms each is parsed separately and merged + # into the final output range list. + + nx = 0 + neterms = 0 + + if (nchars > 0) { + # Get range list storage. + plen = DEF_XLEN + call malloc (ps, plen, TY_PIXEL) + call malloc (pe, plen, TY_PIXEL) + qlen = DEF_XLEN + call malloc (qs, qlen, TY_PIXEL) + call malloc (qe, qlen, TY_PIXEL) + + # Parse each subexpression and merge into output range list. + for (ip=expr; Memc[ip] != EOS; ) { + # Get next subexpression. + while (IS_WHITE (Memc[ip])) + ip = ip + 1 + for (ep=ip; Memc[ip] != EOS; ip=ip+1) + if (Memc[ip] == ';') { + Memc[ip] = EOS + ip = ip + 1 + break + } + if (Memc[ep] == EOS) + break + + # Copy output range list to X list temporary. + if (max(nx,1) > plen) { + plen = max(xlen,1) + call realloc (ps, plen, TY_PIXEL) + call realloc (pe, plen, TY_PIXEL) + } + if (neterms <= 0) { + Mem$t[ps] = LEFT$T + Mem$t[pe] = RIGHT$T + np = 1 + } else { + call amov$t (Mem$t[xs], Mem$t[ps], nx) + call amov$t (Mem$t[xe], Mem$t[pe], nx) + np = nx + } + + # Parse next eterm into Y list temporary. + nq = qpex_parse$t (Memc[ep], qs, qe, qlen) + + # Merge the X and Y lists, leaving result in output list. + nx = qp_rlmerge$t (xs,xe,xlen, + Mem$t[ps], Mem$t[pe], np, Mem$t[qs], Mem$t[qe], nq) + + neterms = neterms + 1 + } + + # Free temporary range list storage. + call mfree (ps, TY_PIXEL); call mfree (pe, TY_PIXEL) + call mfree (qs, TY_PIXEL); call mfree (qe, TY_PIXEL) + + # Convert LEFT/RIGHT magic values to INDEF. + if (nx > 0) { + if (IS_LEFT$T (Mem$t[xs])) + Mem$t[xs] = INDEF + if (IS_RIGHT$T (Mem$t[xe+nx-1])) + Mem$t[xe+nx-1] = INDEF + } + } + + call sfree (sp) + return (nx) +end diff --git a/sys/qpoe/qpexclose.x b/sys/qpoe/qpexclose.x new file mode 100644 index 00000000..137884e9 --- /dev/null +++ b/sys/qpoe/qpexclose.x @@ -0,0 +1,25 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpex.h" + +# QPEX_CLOSE -- Close the QPEX descriptor. + +procedure qpex_close (ex) + +pointer ex #I QPEX descriptor + +pointer lt + +begin + # Free any LUT buffers. + for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) + call mfree (LT_LUTP(lt), TY_SHORT) + + # Free the data and program buffers. + call mfree (EX_PB(ex), TY_STRUCT) + call mfree (EX_DB(ex), TY_CHAR) + + # Free the main descriptor. + call mfree (ex, TY_STRUCT) +end diff --git a/sys/qpoe/qpexcode.gx b/sys/qpoe/qpexcode.gx new file mode 100644 index 00000000..e148b499 --- /dev/null +++ b/sys/qpoe/qpexcode.gx @@ -0,0 +1,484 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given +# expression. The new code is appended to the current compiled program, +# adding additional constraints which a data event will have to meet to +# pass the filter. + +int procedure qpex_codegen$t (ex, atname, assignop, expr, offset, dtype) + +pointer ex #I qpex descriptor +char atname[ARB] #I attribute name (for expr regeneration) +char assignop[ARB] #I "=" or "+=" (for expr regeneration) +char expr[ARB] #I expression to be compiled +int offset #I typed offset of referenced attribute +int dtype #I datatype of referenced attribute + +int nbins, bin, xp +pointer lt, lut, lutx, pb +PIXEL x1, x2, xmin, xmax +int xlen, nranges, n_nranges, level, opcode, ip, i +pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev + +PIXEL sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS] +pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS] +int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS] +int sv_nbins[MAX_LEVELS] + +$if (datatype == d) +double xoffset, xscale +double sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS] +int d_x1, d_x2 +int qpex_refd() +$else +PIXEL d_x1, d_x2 +real xoffset, xscale +real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS] +$endif + +$if (datatype == rd) +bool fp_equal$t() +$else +define fp_equal$t($1==$2) +$endif + +$if (datatype == i) +bool complement +int maskval +int qp_ctoi() +$endif + +int qpex_parse$t() +int stridxs(), btoi(), qpex_sublist$t() +pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos() +errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parse$t + +string qpexwarn "QPEX Warning" +define error_ 91 +define next_ 92 +define null_ 93 +define resume_ 94 +define bbmask_ 95 +define continue_ 96 +define XS Mem$t[xs+($1)-1] +define XE Mem$t[xe+($1)-1] + +begin + pb = EX_PB(ex) + + # Save the program state in case we have to abort. + call qpex_mark (ex, pb_save, db_save) + + # Allocate and initialize a new expression term descriptor, linking + # it onto the tail of the ETTERMs list. + + et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT) + + ET_ATTTYPE(et) = dtype + ET_ATTOFF(et) = offset + ET_ATNAME(et) = qpex_dbpstr (ex, atname) + ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop) + ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr) + ET_PROGPTR(et) = qpex_pbpos (ex) + ET_DELETED(et) = NO + + prev = EX_ETTAIL(ex) + if (prev != NULL) + ET_NEXT(prev) = et + ET_NEXT(et) = NULL + EX_ETTAIL(ex) = et + if (EX_ETHEAD(ex) == NULL) + EX_ETHEAD(ex) = et + + ip = stridxs ("%", expr) + $if (datatype == i) + # Attempt to compile a bitmask test if `%' is found in the + # expression. Since bitmasks cannot be mixed with range list + # expressions, this case is handled separately. + + if (ip > 0) { + complement = false + level = 0 + + # Parse expression (very limited for this case). + for (ip=1; expr[ip] != EOS; ip=ip+1) { + switch (expr[ip]) { + case '!': + complement = !complement + case '(', '[': + level = level + 1 + case ')', ']': + level = level - 1 + case '%': + ip = ip + 1 + if (qp_ctoi (expr, ip, maskval) < 0) + goto bbmask_ + else + ip = ip - 1 + default: + goto bbmask_ + } + } + + # Verify paren level, handle errors. + if (level != 0) { +bbmask_ call eprintf ("%s: bad bitmask expression `%s'\n") + call pargstr (qpexwarn) + call pargstr (expr) + goto error_ + } + + # Compile the bitmask test. + if (complement) + maskval = not(maskval) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, BTTXS, offset, maskval, 0) + else + call qpex_pbpin (ex, BTTXI, offset, maskval, 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = 1 + return (OK) + } + $else + # Bitmask tests are meaningless for floating point data. + if (ip > 0) { + call eprintf ("%s: bitmasks not permitted for floating data\n") + call pargstr (qpexwarn) + goto error_ + } + $endif + + # Compile a general range list expression. The basic procedure is + # to parse the expression to produce an optimized binary range list, + # then either compile the range list as an explicit series of + # instructions or as a lookup table, depending upon the number of + # ranges. + + xlen = DEF_XLEN + call malloc (xs_buf, xlen, TY_PIXEL) + call malloc (xe_buf, xlen, TY_PIXEL) + + # Convert expr to a binary range list and set up the initial context. + # Ensure that the range list buffers are large enough to hold any + # sublists extracted during compilation. + + nranges = qpex_parse$t (expr, xs_buf, xe_buf, xlen) + if (xlen < nranges * 2) { + xlen = nranges * 2 + call realloc (xs_buf, xlen, TY_PIXEL) + call realloc (xe_buf, xlen, TY_PIXEL) + } + + xs = xs_buf + xe = xe_buf + level = 0 + + repeat { +next_ + # Compile a new range list (or sublist). + if (nranges <= 0) { + # This shouldn't happen. +null_ call eprintf ("%s: null range list\n") + call pargstr (qpexwarn) + call qpex_pbpin (ex, PASS, 0, 0, 0) + + } else if (nranges == 1) { + # Output an instruction to load the data, perform the range + # test, and conditionally exit all in a single instruction. + + x1 = XS(1); x2 = XE(1) + $if (datatype == d) + d_x1 = qpex_refd (ex, x1) + d_x2 = qpex_refd (ex, x2) + $else + d_x1 = x1 + d_x2 = x2 + $endif + + if (dtype == TY_SHORT) { + if (IS_LEFT$T(x1) && IS_RIGHT$T(x2)) + ; # pass everything (no tests) + else if (IS_LEFT$T(x1)) + call qpex_pbpin (ex, LEQXS, offset, d_x2, 0) + else if (IS_RIGHT$T(x2)) + call qpex_pbpin (ex, GEQXS, offset, d_x1, 0) + else if (fp_equal$t (x1, x2)) + call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2) + } else { + if (IS_LEFT$T(x1) && IS_RIGHT$T(x2)) + ; # pass everything (no tests) + else if (IS_LEFT$T(x1)) + call qpex_pbpin (ex, LEQX$T, offset, d_x2, 0) + else if (IS_RIGHT$T(x2)) + call qpex_pbpin (ex, GEQX$T, offset, d_x1, 0) + else if (fp_equal$t (x1, x2)) + call qpex_pbpin (ex, EQLX$T, offset, d_x1, d_x2) + else + call qpex_pbpin (ex, RNGX$T, offset, d_x1, d_x2) + } + + } else if (nranges < EX_LUTMINRANGES(ex)) { + # If the number of ranges to be tested for the data is small, + # compile explicit code to perform the range tests directly. + # Otherwise skip forward and compile a lookup table instead. + # In either case, the function of the instructions compiled + # is to test the data loaded into the register above, setting + # the value of PASS to true if the data lies in any of the + # indicated ranges. + + # Check for !X, which is indicated in range list form by a + # two element list bracketing the X on each side. + + if (nranges == 2) + if (IS_LEFT$T(XS(1)) && IS_RIGHT$T(XE(2))) + $if (datatype == si) + if (XE(1)+1 == XS(2)-1) { + if (dtype == TY_SHORT) + opcode = NEQXS + else + opcode = NEQXI + call qpex_pbpin (ex, opcode, offset, XE(1)+1, 0) + goto resume_ + } + $else $if (datatype == r) + if (fp_equal$t (XE(1), XS(2))) { + call qpex_pbpin (ex, NEQX$T, offset, XE(1), 0) + goto resume_ + } + $else + if (fp_equal$t (XE(1), XS(2))) { + call qpex_pbpin (ex, NEQX$T, offset, + qpex_refd(ex,XE(1)), 0) + goto resume_ + } + $endif $endif + + # If at level zero, output instruction to load data into + # register and initialize PASS to false. Don't bother if + # compiling a subprogram, as these operations will already + # have been performed by the caller. + + if (level == 0) { + $if (datatype == i) + if (dtype == TY_SHORT) + opcode = LDSI + else + opcode = LDII + $else + opcode = LD$T$T + $endif + call qpex_pbpin (ex, opcode, offset, 0, 0) + } + + # Compile a series of equality or range tests. + do i = 1, nranges { + x1 = XS(i); x2 = XE(i) + $if (datatype == d) + d_x1 = qpex_refd (ex, x1) + d_x2 = qpex_refd (ex, x2) + $else + d_x1 = x1 + d_x2 = x2 + $endif + + if (IS_LEFT$T(x1)) + call qpex_pbpin (ex, LEQ$T, d_x2, 0, 0) + else if (IS_RIGHT$T(x2)) + call qpex_pbpin (ex, GEQ$T, d_x1, 0, 0) + else if (fp_equal$t (x1, x2)) + call qpex_pbpin (ex, EQL$T, d_x1, d_x2, 0) + else + call qpex_pbpin (ex, RNG$T, d_x1, d_x2, 0) + } + + # Compile a test and exit instruction. + call qpex_pbpin (ex, XIFF, 0, 0, 0) + + } else { + # Compile a lookup table test. Lookup tables may be + # either compressed or fully resolved. If compressed + # (the resolution of the table is less than that of the + # range data, e.g., for floating point lookup tables) a + # LUT bin may have as its value, in addition to the + # usual 0 or 1, the address of an interpreter subprogram + # to be executed to test data values mapping to that bin. + # The subprogram pointed to may in turn be another lookup + # table, hence in the general case a tree of lookup tables + # and little code segments may be compiled to implement + # a complex range list test. + + # Get the data range of the lookup table. + xmin = XS(1) + if (IS_LEFT$T(xmin)) + xmin = XE(1) + xmax = XE(nranges) + if (IS_RIGHT$T(xmax)) + xmax = XS(nranges) + + # Get the lookup table size. Use a fully resolved table + # if the data is integer and the number of bins required + # is modest. + + $if (datatype == i) + nbins = xmax - xmin + 1 + if (nbins > EX_MAXFRLUTLEN(ex)) + nbins = min (EX_MAXRRLUTLEN(ex), + nranges * EX_LUTSCALE(ex)) + $else + nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex)) + $endif + + # Determine the mapping from data space to table space. + xoffset = xmin + $if (datatype == i) + xscale = nbins / (xmax - xmin + 1) + $else + xscale = nbins / (xmax - xmin) + $endif + + # Allocate and initialize the lookup table descriptor. + lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT) + call calloc (lut, nbins, TY_SHORT) + + LT_NEXT(lt) = EX_LTHEAD(ex) + EX_LTHEAD(ex) = lt + LT_TYPE(lt) = TY_PIXEL + LT_LUTP(lt) = lut + LT_NBINS(lt) = nbins + LT_$T0(lt) = xoffset + LT_$TS(lt) = xscale + LT_LEFT(lt) = btoi (IS_LEFT$T(XS(1))) + LT_RIGHT(lt) = btoi (IS_RIGHT$T(XE(nranges))) + + # Compile the LUTX test instruction. Save a back pointer + # to the instruction so that we can edit the jump field in + # case a subprogram is compiled after the LUTXt. + + lutx = qpex_pbpos (ex) + if (dtype == TY_SHORT) + call qpex_pbpin (ex, LUTXS, offset, lt, 0) + else + call qpex_pbpin (ex, LUTX$T, offset, lt, 0) + + xp = 1 + bin = 1 +continue_ + n_xs = xs + nranges + n_xe = xe + nranges + + # Initialize the lookup table. + do i = bin, nbins { + x1 = (i-1) / xscale + xoffset + $if (datatype == i) + x2 = i / xscale + xoffset - 1 + $else + x2 = i / xscale + xoffset + $endif + + # Get sub-rangelist for range x1:x2. + n_nranges = qpex_sublist$t (x1, x2, + Mem$t[xs], Mem$t[xe], nranges, xp, + Mem$t[n_xs], Mem$t[n_xe]) + + if (n_nranges <= 0) { + Mems[lut+i-1] = 0 + + } else if (n_nranges == 1 && IS_LEFT$T(Mem$t[n_xs]) && + IS_RIGHT$T(Mem$t[n_xe])) { + + Mems[lut+i-1] = 1 + + } else { + # Compile the sub-rangelist as a subprogram. + + # First set the LUT bin to point to the subprogram. + # We cannot use the IP directly here since the LUT + # bins are short integer, so store the offset into + # the pb instead (guaranteed to be >= 4). + + Mems[lut+i-1] = qpex_pbpos(ex) - pb + + # Push a new context. + level = level + 1 + if (level > MAX_LEVELS) { + call eprintf ("%s: ") + call pargstr (qpexwarn) + call eprintf ("Excessive LUT nesting\n") + goto error_ + } + + # Save current LUT compilation context. + sv_xs[level] = xs + sv_xe[level] = xe + sv_xp[level] = xp + sv_xoffset[level] = xoffset + sv_xscale[level] = xscale + sv_nranges[level] = nranges + sv_lt[level] = lt + sv_bin[level] = i + sv_nbins[level] = nbins + sv_lut[level] = lut + sv_lutx[level] = lutx + + # Set up context for the new rangelist. + xs = n_xs + xe = n_xe + nranges = n_nranges + + goto next_ + } + } + + # Compile a test and exit instruction if the LUT calls any + # subprograms. + + if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION) + call qpex_pbpin (ex, XIFF, 0, 0, 0) + } +resume_ + # Resume lookup table compilation if exiting due to LUT-bin + # subprogram compilation. + + if (level > 0) { + # Pop saved context. + xs = sv_xs[level] + xe = sv_xe[level] + xp = sv_xp[level] + xoffset = sv_xoffset[level] + xscale = sv_xscale[level] + nranges = sv_nranges[level] + lt = sv_lt[level] + bin = sv_bin[level] + nbins = sv_nbins[level] + lut = sv_lut[level] + lutx = sv_lutx[level] + + # Compile a return from subprogram. + call qpex_pbpin (ex, RET, 0, 0, 0) + + # Patch up the original LUTX instruction to jump over the + # subprogram we have just finished compiling. + + IARG3(lutx) = qpex_pbpos (ex) + + # Resume compilation at the next LUT bin. + bin = bin + 1 + level = level - 1 + goto continue_ + } + } until (level <= 0) + + # Finish setting up the eterm descriptor. + ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION + + return (OK) +error_ + call qpex_free (ex, pb_save, db_save) + return (ERR) +end diff --git a/sys/qpoe/qpexdata.x b/sys/qpoe/qpexdata.x new file mode 100644 index 00000000..1cfc7810 --- /dev/null +++ b/sys/qpoe/qpexdata.x @@ -0,0 +1,210 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <mach.h> +include "qpex.h" + +.help qpexdata +.nf -------------------------------------------------------------------------- +QPEXDATA -- Data management package for QPEX. The QPEX data structures +consist of the QPEX descriptor and two main data buffers, the program buffer +(pb), containing the instructions to be executed (interpreted) to evaluate +an expression, and the data buffer (db), containing assorted data structures, +e.g., the linked list of expression term descriptors, the lookup table +descriptors, storage for DOUBLE data appearing in the compiled expression, +and so on. The program and data buffers are dynamically allocated but are +not relocatable, so to absolute pointers may be used to reference the objects +therein (hence, runtime overflow is possible). + +During expression compilation the following routines are used to add data +objects to the program and data buffers: + + qpex_mark (ex, pb_save, db_save) + qpex_free (ex, pb_save, db_save) + + ip = qpex_pbpos (ex) + qpex_pbpin (ex, opcode, arg1, arg2, arg3) + + ptr = qpex_dbpstr (ex, strval) + intval = qpex_refd (ex, dval) + ptr = qpex_dballoc (ex, nelem, dtype) + +QPEX_MARK and QPEX_FREE are used to mark the current tops of the two buffers +and subspequently free storage back to that point, e.g., for error recovery +following detection of a compilation error. QPEX_PBPOS returns a pointer to +the location in the program buffer where next instruction will be placed. +QPEX_PBPIN compiles an instruction at that location. + +The main storage allocator for the data buffer is QPEX_DBALLOC, which allocates +a properly aligned buffer of the indicated type in the data buffer, and returns +a pointer of the same type as the function value. QPEX_DBPSTR stores a string +constant in the data buffer and returns a pointer to the stored string. +QPEX_REFD stores the given type double constant in the data buffer and returns +(as an integer) a pointer to the stored value (this is necessary to permit +only SZ_INT argument fields in instructions). +.endhelp --------------------------------------------------------------------- + + +# QPEX_MARK -- Mark the top of the program and data buffers. + +procedure qpex_mark (ex, pb_save, db_save) + +pointer ex #I QPEX descriptor +pointer pb_save, db_save #O saved pointers + +begin + pb_save = EX_PBOP(ex) + db_save = EX_DBOP(ex) +end + + +# QPEX_FREE -- Free storage back to the marked points. + +procedure qpex_free (ex, pb_save, db_save) + +pointer ex #I QPEX descriptor +pointer pb_save, db_save #I saved pointers + +pointer top, prev, lt, et +pointer coerce() + +begin + # Free space in program buffer. + call aclri (Memi[pb_save], EX_PBTOP(ex) - pb_save) + EX_PBOP(ex) = pb_save + + # Free space in the data buffer. Prune the LUT and ETERM lists + # and then reset the data buffer pointer. + + # The LT list is backward linked from the most recent entry. + top = coerce (db_save, TY_CHAR, TY_STRUCT) + for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) + if (lt >= top) { + call mfree (LT_LUTP(lt), TY_SHORT) + EX_LTHEAD(ex) = LT_NEXT(lt) + } + + # The ET list is forward linked from the first entry. + prev = NULL + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) + if (et >= top) { + if (prev != NULL) + ET_NEXT(prev) = NULL + EX_ETTAIL(ex) = prev + break + } + + EX_DBOP(ex) = db_save +end + + +# QPEX_PBPOS -- Return a pointer to the program buffer location where the +# next instruction to be compiled will be located. + +pointer procedure qpex_pbpos (ex) + +pointer ex #I QPEX descriptor + +begin + return (EX_PBOP(ex)) +end + + +# QPEX_PBPIN -- Add an insruction at the end of the program buffer. + +procedure qpex_pbpin (ex, opcode, arg1, arg2, arg3) + +pointer ex #I QPEX descriptor +int opcode #I instruction opcode +int arg1,arg2,arg3 #I instruction data fields (typeless) + +pointer op +errchk syserr + +begin + op = EX_PBOP(ex) + if (op >= EX_PBTOP(ex)) + call syserr (SYS_QPEXPBOVFL) + + OPCODE(op) = opcode + IARG1(op) = arg1 + IARG2(op) = arg2 + IARG3(op) = arg3 + + EX_PBOP(ex) = op + LEN_INSTRUCTION +end + + +# QPEX_DBPSTR -- Store a string constant in the data buffer, returning a +# pointer to the stored string as the function value. + +pointer procedure qpex_dbpstr (ex, strval) + +pointer ex #I QPEX descriptor +char strval[ARB] #I string to be stored + +pointer op +int nchars +int strlen() +errchk syserr + +begin + op = EX_DBOP(ex) + nchars = strlen (strval) + 1 + + if (op + nchars >= EX_DBTOP(ex)) + call syserr (SYS_QPEXDBOVFL) + + call strcpy (strval, Memc[op], nchars) + EX_DBOP(ex) = op + nchars + + return (op) +end + + +# QPEX_REFD -- Reference a type DOUBLE datum, returning (as an integer) a +# pointer to the double value, which is stored in the data buffer. + +int procedure qpex_refd (ex, value) + +pointer ex #I QPEX descriptor +double value #I double value + +pointer dp +pointer qpex_dballoc() +errchk qpex_dballoc + +begin + dp = qpex_dballoc (ex, 1, TY_DOUBLE) + Memd[dp] = value + return (dp) +end + + +# QPEX_DBALLOC -- Allocate storage of the indicated type in the data +# buffer, returning a typed pointer to the buffer. The buffer is fully +# aligned. + +pointer procedure qpex_dballoc (ex, nelem, dtype) + +pointer ex #I QPEX descriptor +int nelem #I amount of storage desired +int dtype #I datatype of the storage element + +pointer op, top +pointer coerce() +int sizeof() +errchk syserr + +begin + op = EX_DBOP(ex) + while (mod (op-1, SZ_DOUBLE) != 0) + op = op + 1 + + top = op + nelem * sizeof(dtype) + if (top >= EX_DBTOP(ex)) + call syserr (SYS_QPEXDBOVFL) + + EX_DBOP(ex) = top + return (coerce (op, TY_CHAR, dtype)) +end diff --git a/sys/qpoe/qpexdebug.x b/sys/qpoe/qpexdebug.x new file mode 100644 index 00000000..26f63e8b --- /dev/null +++ b/sys/qpoe/qpexdebug.x @@ -0,0 +1,441 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpexset.h> +include <mach.h> +include "qpex.h" +include "qpoe.h" + +define NLUTPERLINE 15 +define SZ_TEXT 4 +define SZ_FILTERBUF 32768 + +# QPEX_DEBUG -- Output text describing the state and contents of the QPEX +# descriptor (compiled event attribute filter). + +procedure qpex_debug (ex, out, what) + +pointer ex #I QPEX descriptor +int out #I output stream +int what #I bitflags defining what to print + +char binval[SZ_TEXT] +pointer sp, text, label, lutp, et, lt, pb, ip +int neterms, lutno, proglen, dest, nout, ch, i +int qpex_getfilter() +define lut_ 91 + +begin + call smark (sp) + + neterms = 0 + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) + neterms = neterms + 1 + proglen = (EX_PBOP(ex) - EX_PB(ex)) / LEN_INSTRUCTION + + # Print summary information. + if (and (what, QPEXD_SUMMARY) != 0) { + call fprintf (out, + "QPEX_DEBUG: ex=%xX, neterms=%d, proglen=%d/%d\n") + call pargi (ex) + call pargi (neterms) + call pargi (proglen) + call pargi ((EX_PBTOP(ex) - EX_PB(ex)) / LEN_INSTRUCTION) + + call fprintf (out, "pb=%xX, pbtop=%xX, pbop=%xX, start=%xX\n") + call pargi (EX_PB(ex)) + call pargi (EX_PBTOP(ex)) + call pargi (EX_PBOP(ex)) + call pargi (EX_START(ex)) + + call fprintf (out, "db=%xX, dbtop=%xX, dbop=%xX, datalen=%d/%d\n") + call pargi (EX_DB(ex)) + call pargi (EX_DBTOP(ex)) + call pargi (EX_DBOP(ex)) + call pargi (EX_DBOP(ex) - EX_DB(ex)) + call pargi (EX_DBTOP(ex) - EX_DB(ex)) + + call fprintf (out, "max_frlutlen=%d, max_rrlutlen=%d, ") + call pargi (EX_MAXFRLUTLEN(ex)) + call pargi (EX_MAXRRLUTLEN(ex)) + call fprintf (out, "lut_scale=%d, lut_minranges=%d\n") + call pargi (EX_LUTSCALE(ex)) + call pargi (EX_LUTMINRANGES(ex)) + + call fprintf (out, "ethead=%xX, ettail=%xX, lthead=%xX\n") + call pargi (EX_ETHEAD(ex)) + call pargi (EX_ETTAIL(ex)) + call pargi (EX_LTHEAD(ex)) + } + + # Regenerate and print the compiled expression. + if (and (what, QPEXD_SHOWEXPR) != 0) { + call salloc (text, SZ_FILTERBUF, TY_CHAR) + call fprintf (out, + "==================== expr ========================\n") + if (qpex_getfilter (ex, Memc[text], SZ_FILTERBUF) > 0) { + call putline (out, Memc[text]) + call fprintf (out, "\n") + } + } + + # Decode the compiled program (print assembled instructions). + if (and (what, QPEXD_PROGRAM) != 0) { + pb = EX_PB(ex) + + call salloc (label, proglen+1, TY_INT) + call aclri (Memi[label], proglen+1) + + # Flag those instructions which are the destinations of branches. + do i = 1, proglen { + ip = pb + (i - 1) * LEN_INSTRUCTION + switch (OPCODE(ip)) { + case GOTO: + dest = (IARG1(ip) - pb) / LEN_INSTRUCTION + Memi[label+dest] = YES + case LUTXS, LUTXI, LUTXR, LUTXD: + if (IARG3(ip) == NULL) + dest = i + else + dest = (IARG3(ip) - pb) / LEN_INSTRUCTION + Memi[label+dest] = YES + } + } + + # Do the same for code segments pointed to by lookup tables. + for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) { + lutp = LT_LUTP(lt) + do i = 0, LT_NBINS(lt) - 1 { + dest = Mems[lutp+i] + if (dest > 1) { + dest = dest / LEN_INSTRUCTION + Memi[label+dest] = YES + } + } + } + + # Output the program. + call fprintf (out, + "==================== program =====================\n") + + do i = 1, proglen + 1 { + ip = pb + (i - 1) * LEN_INSTRUCTION + + # Output instruction label if target of a branch. + if (Memi[label+i-1] == YES) { + call fprintf (out, "L%d:\t") + call pargi (i) + } else + call fprintf (out, "\t") + + # Decode and output the instruction itself. + switch (OPCODE(ip)) { + case NOP: + call fprintf (out, "nop") + case GOTO: + dest = (IARG1(ip) - pb) / LEN_INSTRUCTION + 1 + call fprintf (out, "goto L%d") + call pargi (dest) + case XIFT: + call fprintf (out, "xift") + case XIFF: + call fprintf (out, "xiff") + case PASS: + call fprintf (out, "pass") + case RET: + call fprintf (out, "ret") + + case LDSI: + call fprintf (out, "ldsi\t(%d)") + call pargi (IARG1(ip)) + case LDII: + call fprintf (out, "ldii\t(%d)") + call pargi (IARG1(ip)) + case LDRR: + call fprintf (out, "ldrr\t(%d)") + call pargi (IARG1(ip)) + case LDRD: + call fprintf (out, "ldrd\t(%d)") + call pargi (IARG1(ip)) + case LDDD: + call fprintf (out, "lddd\t(%d)") + call pargi (IARG1(ip)) + + case BTTI: + call fprintf (out, "btti\t%oB") + call pargi (IARG1(ip)) + case EQLI: + call fprintf (out, "eqli\t%d") + call pargi (IARG1(ip)) + case EQLR: + call fprintf (out, "eqlr\t%g") + call pargr (RARG1(ip)) + case EQLD: + call fprintf (out, "eqld\t%g") + call pargd (DARG1(ip)) + case LEQI: + call fprintf (out, "leqi\t%d") + call pargi (IARG1(ip)) + case LEQR: + call fprintf (out, "leqr\t%g") + call pargr (RARG1(ip)) + case LEQD: + call fprintf (out, "leqd\t%g") + call pargd (DARG1(ip)) + case GEQI: + call fprintf (out, "geqi\t%d") + call pargi (IARG1(ip)) + case GEQR: + call fprintf (out, "geqr\t%g") + call pargr (RARG1(ip)) + case GEQD: + call fprintf (out, "geqd\t%g") + call pargd (DARG1(ip)) + + case RNGI: + call fprintf (out, "rngi\t%d, %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case RNGR: + call fprintf (out, "rngr\t%g, %g") + call pargr (RARG1(ip)) + call pargr (RARG2(ip)) + case RNGD: + call fprintf (out, "rngd\t%g, %g") + call pargd (DARG1(ip)) + call pargd (DARG2(ip)) + + case BTTXS: + call fprintf (out, "bttxs\t(%d), %oB") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case BTTXI: + call fprintf (out, "bttxi\t(%d), %oB") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + + case NEQXS: + call fprintf (out, "neqxs\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case NEQXI: + call fprintf (out, "neqxi\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case NEQXR: + call fprintf (out, "neqxr\t(%d), %g") + call pargi (IARG1(ip)) + call pargr (RARG2(ip)) + case NEQXD: + call fprintf (out, "neqxd\t(%d), %g") + call pargi (IARG1(ip)) + call pargd (DARG2(ip)) + + case EQLXS: + call fprintf (out, "eqlxs\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case EQLXI: + call fprintf (out, "eqlxi\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case EQLXR: + call fprintf (out, "eqlxr\t(%d), %g") + call pargi (IARG1(ip)) + call pargr (RARG2(ip)) + case EQLXD: + call fprintf (out, "eqlxd\t(%d), %g") + call pargi (IARG1(ip)) + call pargd (DARG2(ip)) + + case LEQXS: + call fprintf (out, "leqxs\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case LEQXI: + call fprintf (out, "leqxi\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case LEQXR: + call fprintf (out, "leqxr\t(%d), %g") + call pargi (IARG1(ip)) + call pargr (RARG2(ip)) + case LEQXD: + call fprintf (out, "leqxd\t(%d), %g") + call pargi (IARG1(ip)) + call pargd (DARG2(ip)) + + case GEQXS: + call fprintf (out, "geqxs\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case GEQXI: + call fprintf (out, "geqxi\t(%d), %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + case GEQXR: + call fprintf (out, "geqxr\t(%d), %g") + call pargi (IARG1(ip)) + call pargr (RARG2(ip)) + case GEQXD: + call fprintf (out, "geqxd\t(%d), %g") + call pargi (IARG1(ip)) + call pargd (DARG2(ip)) + + case RNGXS: + call fprintf (out, "rngxs\t(%d), %d, %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + call pargi (IARG3(ip)) + case RNGXI: + call fprintf (out, "rngxi\t(%d), %d, %d") + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + call pargi (IARG3(ip)) + case RNGXR: + call fprintf (out, "rngxr\t(%d), %g, %g") + call pargi (IARG1(ip)) + call pargr (RARG2(ip)) + call pargr (RARG3(ip)) + case RNGXD: + call fprintf (out, "rngxd\t(%d), %g, %g") + call pargi (IARG1(ip)) + call pargd (DARG2(ip)) + call pargd (DARG3(ip)) + + case LUTXS: + ch = 's' + goto lut_ + case LUTXI: + ch = 'i' + goto lut_ + case LUTXR: + ch = 'r' + goto lut_ + case LUTXD: + ch = 'd' +lut_ call fprintf (out, "lutx%c\t(%d), %xX, L%d") + call pargi (ch) + call pargi (IARG1(ip)) + call pargi (IARG2(ip)) + if (IARG3(ip) != NULL) + call pargi ((IARG3(ip) - pb) / LEN_INSTRUCTION + 1) + else + call pargi (i + 1) + } + + call fprintf (out, "\n") + } + } + + # Output expression terms list. + if (and (what, QPEXD_ETLIST) != 0) { + call fprintf (out, + "==================== eterms ======================\n") + if (neterms > 0) { + call fprintf (out, + " N TYPE OFF IP LEN DEL ATTRIBUTE OP EXPR\n") + neterms = 0 + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) { + neterms = neterms + 1 + call fprintf (out, + "%2d %4d %3d %3d %4d %3d %9.9s %2s ") + call pargi (neterms) + call pargi (ET_ATTTYPE(et)) + call pargi (ET_ATTOFF(et)) + call pargi ((ET_PROGPTR(et) - pb) / LEN_INSTRUCTION + 1) + call pargi (ET_NINSTR(et)) + call pargi (ET_DELETED(et)) + call pargstr (Memc[ET_ATNAME(et)]) + call pargstr (Memc[ET_ASSIGNOP(et)]) + call putline (out, Memc[ET_EXPRTEXT(et)]) + call putline (out, "\n") + } + } + } + + # Output lookup table list. + if (and (what, QPEXD_LTLIST+QPEXD_SHOWLUTS) != 0) { + if (EX_LTHEAD(ex) != NULL) { + call fprintf (out, + "==================== lutlist =====================\n") + + # Output column labels. + call fprintf (out, + " N LT LUTP TYPE NBINS L R %*wZERO SCALE\n") + if (LT_TYPE(EX_LTHEAD(ex)) == TY_DOUBLE) + call pargi (NDIGITS_DP - 4) + else + call pargi (NDIGITS_RP - 4) + + # Output lookup table descriptors. + lutno = 0 + for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) { + lutno = lutno + 1 + call fprintf (out, "%2d %6x %6x %4d %5d %d %d %*g %g\n") + call pargi (lutno) + call pargi (lt) + call pargi (LT_LUTP(lt)) + call pargi (LT_TYPE(lt)) + call pargi (LT_NBINS(lt)) + call pargi (LT_LEFT(lt)) + call pargi (LT_RIGHT(lt)) + + switch (LT_TYPE(lt)) { + case TY_INT: + call pargi (NDIGITS_RP) + call pargr (LT_I0(lt)) + call pargr (LT_IS(lt)) + case TY_REAL: + call pargi (NDIGITS_RP) + call pargr (LT_R0(lt)) + call pargr (LT_RS(lt)) + case TY_DOUBLE: + call pargi (NDIGITS_DP) + call pargd (LT_D0(lt)) + call pargd (LT_DS(lt)) + } + } + } + + # Dump the lookup table data. + if (and (what, QPEXD_SHOWLUTS) != 0) { + lutno = 0 + for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) { + lutno = lutno + 1 + lutp = LT_LUTP(lt) + call fprintf (out, + "================== LUT %d (%x) ==================\n") + call pargi (lutno) + call pargi (lutp) + nout = 0 + do i = 0, LT_NBINS(lt) - 1 { + if (i == 0 || nout >= NLUTPERLINE) { + if (i > 0) + call fprintf (out, "\n") + call fprintf (out, "%04d") + call pargi (i) + nout = 0 + } + + # Print the bin value as 0, 1, or a statement label. + dest = Mems[lutp+i] + if (dest <= 1) { + call fprintf (out, " %4d") + call pargi (dest) + } else { + call sprintf (binval, SZ_TEXT, "L%d") + call pargi (dest / LEN_INSTRUCTION + 1) + call fprintf (out, " %4s") + call pargstr (binval) + } + + nout = nout + 1 + } + if (nout > 0) + call fprintf (out, "\n") + } + } + } + + call sfree (sp) +end diff --git a/sys/qpoe/qpexdel.x b/sys/qpoe/qpexdel.x new file mode 100644 index 00000000..ce4da432 --- /dev/null +++ b/sys/qpoe/qpexdel.x @@ -0,0 +1,58 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpex.h" + +# QPEX_DELETE -- Delete any previously compiled expression terms for the +# event attribute with the given offset and datatype. Only terms up to and +# including ET_LAST are affected (allowing deletion while compiling additional +# terms). + +procedure qpex_delete (ex, et_last, offset, dtype) + +pointer ex #I QPEX descriptor +pointer et_last #I last expression term to be edited +int offset #I typed offset of attribute in event struct +int dtype #I datatype of attribute + +pointer et, ip +int ninstr, i + +begin + if (et_last == NULL) + return + + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) { + # Skip over already deleted terms or terms for other attributes. + if (ET_DELETED(et) == YES) + next + else if (ET_ATTOFF(et) != offset || ET_ATTTYPE(et) != dtype) + next + + # Physically and logically delete the term. Edit the program + # buffer and replace the compiled sequence of instructions by + # a GOTO followed by a series of NO-OPs. + + ip = ET_PROGPTR(et) + ninstr = ET_NINSTR(et) + + OPCODE(ip) = GOTO + IARG1(ip) = ip + ninstr * LEN_INSTRUCTION + IARG2(ip) = NULL + IARG3(ip) = NULL + + do i = 2, ninstr { + ip = ET_PROGPTR(et) + (i-1) * LEN_INSTRUCTION + OPCODE(ip) = NOP + IARG1(ip) = NULL + IARG2(ip) = NULL + IARG3(ip) = NULL + } + + # Flag the eterm as deleted. + ET_DELETED(et) = YES + + if (et == et_last) + break + } +end diff --git a/sys/qpoe/qpexeval.x b/sys/qpoe/qpexeval.x new file mode 100644 index 00000000..5e120296 --- /dev/null +++ b/sys/qpoe/qpexeval.x @@ -0,0 +1,362 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpex.h" + +define RTOL (EPSILONR * 10.0) # (only useful for normalized numbers) +define DTOL (EPSILOND * 10.0) + +# QPEX_EVALUATE -- Evaluate the compiled event-attribute expression for the +# given seqeuence of event structs. Expression evaluation for each event +# terminates as soon as an attribute test fails. If all attribute tests +# succeed (i.e., the full expression is evaluated or the evaluate function +# runs to completion) then the event pointer is put on the output list O_EV, +# indicating that the event satisfies the given selection expression. +# The function value is the number of events which passed the filter. +# The time required to evaluate an expression depends upon the complexity of +# the expression to be evaluated, and the fraction of events which fail the +# test (a fail is determined quicker than a pass - attribute tests likely to +# fail should appear first in the expression). + +int procedure qpex_evaluate (ex, i_ev, o_ev, nev) + +pointer ex #I QPEX descriptor (expression) +pointer i_ev[nev] #I array of pointers to event structs +pointer o_ev[nev] #O receives the pointers of the passed events +int nev #I number of input events + +int i0 # integer data register +real r0 # real data register +double d0 # double data register +bool pass # expression value +int npass # number of events which pass expr + +real rbin +bool pv_save[MAX_LEVELS] +pointer ip_save[MAX_LEVELS] +pointer lt, ev, ev_i, ev_r, ev_d, ip +int level, bin, i, j, v + +define lut_ 91 +define ret_ 92 +define ev_s ev + +begin + npass = 0 + + do j = 1, nev { + pass = false + ev = i_ev[j] + + # Get event struct pointers of various types. + ev_d = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1 + ev_i = (ev - 1) * SZ_SHORT / SZ_INT + 1 + ev_r = ev_i + + # Execute each compiled instruction in sequence until the value + # of the compiled attribute-value expression is known. The call + # stack level is used to keep track of subroutine calls + # (subroutines are used to evaluate the indeterminate cells of + # compressed lookup tables). + + # Notes on expression evaluation. + # --------------------------------- + # An expression consists of 1 or more expression terms, all of + # which must pass the event for the event to pass the filter. + # + # An expression term consists of a range list giving a list of + # acceptable values or ranges of values. + # + # The compiled expression consists of a sequence of instruction + # blocks, one for each expression term. If the event fails to + # pass any expression term (instruction block) then the event + # fails and we are done. Instruction blocks are of three types: + # + # 1) Multiple instructions consisting of a load register, + # any number of register tests, then a XIFF or XIFT + # test at the end of the block. PASS is set to false + # at the beginning of the block and can be set to true + # by any register test to pass the event to the next + # expression term. + # + # 2) In simple cases the above can all be expressed as a + # single test-and-exit-if-false instruction. These are + # the "X" instructions below. + # + # 3) The lookup table (LUTX) instruction. LUTX is like + # case 2) except that it may compile as a sequence of + # many instructions, using subprograms to evaluate the + # value of LUT bins. LUTs may nest. When lookup table + # evaluation is complete the instruction branches + # forward to a closing XIFF which is used to test the + # value of PASS returned by the executed LUT-bin + # subprograms. + # + # The blocks of instructions corresponding to successive expression + # terms are executed until the PASS instruction is encountered. + # Execution of PASS terminates evaluation and passes the event. + + ip = EX_START(ex) + level = 0 + + do i = 1, MAX_INSTRUCTIONS { + pragma switch_no_range_check + switch (OPCODE(ip)) { + case NOP: # null operation + ; + case GOTO: # go-to prog offset + ip = IARG1(ip) + next + case XIFT: # exit if true + if (pass) { + pass = false + goto ret_ + } + case XIFF: # exit if false + if (!pass) + goto ret_ + case PASS: + pass = true + break + case RET: # return from subprog +ret_ if (level > 0) { + pass = (pv_save[level] || pass) + ip = ip_save[level] + level = level - 1 + next + } else + break + + case LDSI: # load registers + i0 = Mems[ev_s+IARG1(ip)] + pass = false + case LDII: + i0 = Memi[ev_i+IARG1(ip)] + pass = false + case LDRR: + r0 = Memr[ev_r+IARG1(ip)] + pass = false + case LDRD: + d0 = Memr[ev_r+IARG1(ip)] + pass = false + case LDDD: + d0 = Memd[ev_d+IARG1(ip)] + pass = false + + case BTTI: # register tests + pass = pass || (and (i0, IARG1(ip)) != 0) + case EQLI: + pass = pass || (i0 == IARG1(ip)) + case EQLR: + pass = pass || (abs(r0 - RARG1(ip)) < RTOL) + case EQLD: + pass = pass || (abs(d0 - DARG1(ip)) < DTOL) + case LEQI: + pass = pass || (i0 <= IARG1(ip)) + case LEQR: + pass = pass || (r0 <= RARG1(ip)) + case LEQD: + pass = pass || (d0 <= DARG1(ip)) + case GEQI: + pass = pass || (i0 >= IARG1(ip)) + case GEQR: + pass = pass || (r0 >= RARG1(ip)) + case GEQD: + pass = pass || (d0 >= DARG1(ip)) + case RNGI: + pass = pass || (i0 >= IARG1(ip) && i0 <= IARG2(ip)) + case RNGR: + pass = pass || (r0 >= RARG1(ip) && r0 <= RARG2(ip)) + case RNGD: + pass = pass || (d0 >= DARG1(ip) && d0 <= DARG2(ip)) + + case BTTXS: # load, test, and + i0 = Mems[ev_s+IARG1(ip)] # exit if false + pass = (and (i0, IARG2(ip)) != 0) + if (!pass) + goto ret_ + case BTTXI: + pass = (and (Memi[ev_i+IARG1(ip)], IARG2(ip)) != 0) + if (!pass) + goto ret_ + + case NEQXS: + pass = (Mems[ev_s+IARG1(ip)] != IARG2(ip)) + if (!pass) + goto ret_ + case NEQXI: + pass = (Memi[ev_i+IARG1(ip)] != IARG2(ip)) + if (!pass) + goto ret_ + case NEQXR: + pass = (abs(Memr[ev_r+IARG1(ip)] - RARG2(ip)) > RTOL) + if (!pass) + goto ret_ + case NEQXD: + pass = (abs(Memd[ev_d+IARG1(ip)] - DARG2(ip)) > DTOL) + if (!pass) + goto ret_ + + case EQLXS: + pass = (Mems[ev_s+IARG1(ip)] == IARG2(ip)) + if (!pass) + goto ret_ + case EQLXI: + pass = (Memi[ev_i+IARG1(ip)] == IARG2(ip)) + if (!pass) + goto ret_ + case EQLXR: + pass = (abs(Memr[ev_r+IARG1(ip)] - RARG2(ip)) <= RTOL) + if (!pass) + goto ret_ + case EQLXD: + pass = (abs(Memd[ev_d+IARG1(ip)] - DARG2(ip)) <= DTOL) + if (!pass) + goto ret_ + + case LEQXS: + pass = (Mems[ev_s+IARG1(ip)] <= IARG2(ip)) + if (!pass) + goto ret_ + case LEQXI: + pass = (Memi[ev_i+IARG1(ip)] <= IARG2(ip)) + if (!pass) + goto ret_ + case LEQXR: + pass = (Memr[ev_r+IARG1(ip)] <= RARG2(ip)) + if (!pass) + goto ret_ + case LEQXD: + pass = (Memd[ev_d+IARG1(ip)] <= DARG2(ip)) + if (!pass) + goto ret_ + + case GEQXS: + pass = (Mems[ev_s+IARG1(ip)] >= IARG2(ip)) + if (!pass) + goto ret_ + case GEQXI: + pass = (Memi[ev_i+IARG1(ip)] >= IARG2(ip)) + if (!pass) + goto ret_ + case GEQXR: + pass = (Memr[ev_r+IARG1(ip)] >= RARG2(ip)) + if (!pass) + goto ret_ + case GEQXD: + pass = (Memd[ev_d+IARG1(ip)] >= DARG2(ip)) + if (!pass) + goto ret_ + + case RNGXS: + i0 = Mems[ev_s+IARG1(ip)] + pass = (i0 >= IARG2(ip) && i0 <= IARG3(ip)) + if (!pass) + goto ret_ + case RNGXI: + i0 = Memi[ev_i+IARG1(ip)] + pass = (i0 >= IARG2(ip) && i0 <= IARG3(ip)) + if (!pass) + goto ret_ + case RNGXR: + r0 = Memr[ev_r+IARG1(ip)] + pass = (r0 >= RARG2(ip) && r0 <= RARG3(ip)) + if (!pass) + goto ret_ + case RNGXD: + d0 = Memd[ev_d+IARG1(ip)] + pass = (d0 >= DARG2(ip) && d0 <= DARG3(ip)) + if (!pass) + goto ret_ + + case LUTXS: # lookup tables + i0 = Mems[ev_s+IARG1(ip)] + lt = IARG2(ip) + rbin = (i0 - int(LT_I0(lt))) * LT_IS(lt) + goto lut_ + case LUTXI: + i0 = Memi[ev_i+IARG1(ip)] + lt = IARG2(ip) + rbin = (i0 - int(LT_I0(lt))) * LT_IS(lt) + goto lut_ + case LUTXR: + r0 = Memr[ev_r+IARG1(ip)] + lt = IARG2(ip) + rbin = (r0 - LT_R0(lt)) * LT_RS(lt) + goto lut_ + case LUTXD: + d0 = Memd[ev_d+IARG1(ip)] + lt = IARG2(ip) + rbin = (d0 - LT_D0(lt)) * LT_DS(lt) +lut_ + # Common code for any lookup table. + if (rbin <= 0) + v = LT_LEFT(lt) + else { + bin = int(rbin) + 1 + if (bin > LT_NBINS(lt)) + v = LT_RIGHT(lt) + else + v = LT_LUT(lt,bin) + } + + # Table value may be 0, 1, or indeterminate, i.e., the + # offset of a subprogram to be called to evaluate the + # subrangelist for that bin. + + if (v == 0) { + # Table value is zero, !pass, all done. + pass = false + goto ret_ + + } else if (v > 1) { + # Table value is indeterminate and depends on the + # data value. Call subroutine to evaluate subrange. + # At level=0 where we are starting to evaluate an + # independent expression term we must initialize pass + # to false before entering the subprogram instruction + # sequence. + + if (level == 0) + pass = false + + level = level + 1 + pv_save[level] = pass + + if (IARG3(ip) != NULL) + ip_save[level] = IARG3(ip) + else + ip_save[level] = ip + LEN_INSTRUCTION + + pass = false + ip = EX_PB(ex) + v + next + + } else if (v == 1) { + # Table value is one, value passes this test. + pass = true + } + + # Go to the jump address if set. The jump is needed + # to skip over any subprograms that may have been compiled + # after the LUTX. + + if (IARG3(ip) != NULL) { + ip = IARG3(ip) + next + } + } + + # Advance to the next instruction. + ip = ip + LEN_INSTRUCTION + } + + # Output event pointer if event passed the filter. + if (pass) { + npass = npass + 1 + o_ev[npass] = ev + } + } + + return (npass) +end diff --git a/sys/qpoe/qpexgetat.x b/sys/qpoe/qpexgetat.x new file mode 100644 index 00000000..9f7b3af2 --- /dev/null +++ b/sys/qpoe/qpexgetat.x @@ -0,0 +1,61 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpex.h" + +# QPEX_GETATTRIBUTE -- Get the filter expression for the named attribute +# as a text string. The length of the string is returned as the function +# value. If the referenced QPEX descriptor does not contain any filter +# terms for the named attribute, zero will be returned. If the expression +# contains multiple eterms the successive terms will be delimited by +# semicolons, e.g., "(a:b,c:d); (e:f,g)". The lists of ranges within an +# eterm are OR-ed to produce a filter term; successive eterms are AND-ed +# to produce the final filter (hence the example above is equivalent to +# "(a to b OR c to d) AND (e to f OR g)"). + +int procedure qpex_getattribute (ex, attribute, outstr, maxch) + +pointer ex #I QPEX descriptor +char attribute[ARB] #I attribute name +char outstr[maxch] #O receives the filter string +int maxch #I max chars out + +pointer sp, atname, et +int nchars, op, otop +int gstrcpy(), qp_expandtext() +bool strne() + +begin + call smark (sp) + call salloc (atname, SZ_FNAME, TY_CHAR) + + # Translate attribute name, in case it is aliased. + nchars = qp_expandtext (EX_QP(ex), attribute, Memc[atname], SZ_FNAME) + + # Construct filter expression for named attribute. + op = 1 + otop = maxch + 1 + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) { + if (ET_DELETED(et) == YES) + next + + # Skip entry if not for the named attribute. + if (strne (Memc[ET_ATNAME(et)], Memc[atname])) + next + + # Add term delimiter if not first term. + if (op > 1) { + outstr[op] = ';'; op = min(otop, op + 1) + outstr[op] = ' '; op = min(otop, op + 1) + } + + # The expression text (may be very large). + op = min (otop, + op + gstrcpy (Memc[ET_EXPRTEXT(et)], outstr[op], otop-op)) + } + outstr[op] = EOS + + # Return the string length, or zero if no filter for named attribute. + call sfree (sp) + return (op - 1) +end diff --git a/sys/qpoe/qpexgetfil.x b/sys/qpoe/qpexgetfil.x new file mode 100644 index 00000000..3f1d2816 --- /dev/null +++ b/sys/qpoe/qpexgetfil.x @@ -0,0 +1,50 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpex.h" + +# QPEX_GETFILTER -- Return the currently active filter as a text string, +# i.e., as a series of "attribute = expr" terms. The number of chars +# output is returned as the function value. + +int procedure qpex_getfilter (ex, outstr, maxch) + +pointer ex #I QPEX descriptor +char outstr[maxch] #O receives the filter string +int maxch #I max chars out + +pointer et +int op, otop +int gstrcpy() + +begin + op = 1 + otop = maxch + 1 + for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) { + if (ET_DELETED(et) == YES) + next + + # Add term delimiter if not first term. + if (op > 1) { + outstr[op] = ','; op = min(otop, op + 1) + outstr[op] = ' '; op = min(otop, op + 1) + } + + # Attribute name. + op = min (otop, + op + gstrcpy (Memc[ET_ATNAME(et)], outstr[op], otop-op)) + outstr[op] = ' '; op = min(otop, op + 1) + + # Assignment operator ("=" or "+="). + op = min (otop, + op + gstrcpy (Memc[ET_ASSIGNOP(et)], outstr[op], otop-op)) + outstr[op] = ' '; op = min(otop, op + 1) + + # The expression text (may be very large). + op = min (otop, + op + gstrcpy (Memc[ET_EXPRTEXT(et)], outstr[op], otop-op)) + } + outstr[op] = EOS + + return (op - 1) +end diff --git a/sys/qpoe/qpexmodfil.x b/sys/qpoe/qpexmodfil.x new file mode 100644 index 00000000..7df4b926 --- /dev/null +++ b/sys/qpoe/qpexmodfil.x @@ -0,0 +1,247 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpoe.h" +include "qpex.h" + +# QPEX_MODFILTER -- Compile an event attribute expression to be used for event +# attribute filtering, modifying the the current EAF as directed by the expr. +# An event attribute expression consists of a sequence of independent terms +# of the form "attribute = expr", e.g., +# +# pha=%104B, e=100:, t=(:9,11:29,33,42:65,67:99,!(82,87),103), ... +# +# +# Variants on "attr = expr" are "attr := expr" and "attr += expr". In the +# case of :=, any expression terms already entered for the named attribute +# will be REPLACED by the new expression. In the case of +=, the given +# expression denotes an additional condition which the attribute must satisfy +# to pass the filter, i.e., a new term is added to the existing filter. The +# case = is the same as +=, i.e., the default action is to modify rather than +# replace any existing filter. +# +# +# Our function is to extract each attribute=expr term and compile it into a +# series of instructions to be repeatedly executed (interpreted) at runtime +# to evaluate the expression for a particular event structure. Terms are +# compiled and evaluated in the order in which they appear in the expression +# list (except for replacement terms), allowing the user to manually optimize +# the filter by giving terms which are most likely to fail first. +# +# The expression list may contain references to predefined global or local +# (datafile) macros, external macro files, or back-quoted CL commands for +# which the output is to be substituted as for a macro. In all cases, macro +# substitution is handled at a lower level in the gettok routine. In +# particular, the logical names of the fields of the event structure are +# implemented as predefined datafile-local macros, hence we are concerned only +# with physical field names here. The form of a physical field name is +# a datatype code [SIRD] followed by the decimal zero-indexed byte offset +# of the field in the event structure, e.g., S0, S2, R4, etc. (short integer +# field at offset 0, same at offset 2, Real*4 field at offset 4, etc.). +# +# The function value is OK if the expression list compiles without any errors, +# or ERR if some compilation error occurs. Compilation errors cause an error +# message to be output to STDERR and the affected terms to be skipped, but are +# otherwise ignored. + +int procedure qpex_modfilter (ex, exprlist) + +pointer ex #I qpex descriptor +char exprlist[ARB] #I list of attribute=expr expressions + +bool replace +int boffset, offset, max_offset, dtype +int status, sz_expr, token, parenlevel, nchars, buflen +pointer sp, atname, assignop, tokbuf, expr, qp, ip, op, in, et_tail + +pointer qp_opentext() +int qpex_codegeni(), qpex_codegenr(), qpex_codegend() +int qp_gettok(), strlen(), gstrcpy(), ctoi(), sizeof() +errchk malloc, qp_opentext, qp_gettok, realloc, qpex_delete + +string qpexwarn "QPEX Warning" +define eatup_ 91 +define badatt_ 92 + +begin + call smark (sp) + call salloc (atname, SZ_TOKBUF, TY_CHAR) + call salloc (assignop, SZ_TOKBUF, TY_CHAR) + call salloc (tokbuf, SZ_TOKBUF, TY_CHAR) + + status = OK + sz_expr = DEF_SZEXPRBUF + et_tail = EX_ETTAIL(ex) + qp = EX_QP(ex) + + # Allocate a variable size expression buffer. + call malloc (expr, sz_expr, TY_CHAR) + + # Open the expression list for token input with macro expansion. + in = qp_opentext (qp, exprlist) + + # Accumulate and compile successive attribute=expr terms of the + # expression list. + + repeat { + # Get attribute name. + switch (qp_gettok (in, Memc[atname], SZ_TOKBUF)) { + case EOF: + break # input exhausted + case ',', ';': + next # null statement + case TOK_IDENTIFIER: + ; # got one + default: + call eprintf ("%s: unexpected token `%s'\n") + call pargstr (qpexwarn) + call pargstr (Memc[atname]) + goto eatup_ + } + + # Get operator. + switch (qp_gettok (in, Memc[assignop], SZ_TOKBUF)) { + case TOK_PLUSEQUALS, '=': + replace = false + case TOK_COLONEQUALS: + replace = true + + default: + call eprintf ("%s: missing assignment token (`%s')\n") + call pargstr (qpexwarn) + call pargstr (Memc[atname]) +eatup_ + # A half-hearted attempt to ignore the offending statement... + while (qp_gettok (in, Memc[expr], sz_expr) != EOF) + if (Memc[expr] == ',') + break + + # The default is to add to any existing filter. + replace = false + } + + parenlevel = 0 + token = NULL + + # Accumulate expression. + for (op=expr; token != EOF; ) { + # Get next token from input stream. + token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) + + # Process any special tokens. + switch (token) { + case EOF: + break + case '(': + parenlevel = parenlevel + 1 + case ')': + parenlevel = parenlevel - 1 + if (parenlevel < 0) { + call eprintf ("%s: missing left parenthesis\n") + call pargstr (qpexwarn) + parenlevel = 0 + status = ERR + next + } + case ',', ';': + # An unparenthesized comma terminates the expression. + if (parenlevel <= 0) + break + } + + # Allocate more storage if expr buf fills. + nchars = strlen (Memc[tokbuf]) + buflen = op - expr + if (buflen + nchars > sz_expr) { + sz_expr = sz_expr + INC_SZEXPRBUF + call realloc (expr, sz_expr, TY_CHAR) + op = expr + buflen + } + + # Concatenate token string to expr. + op = op + gstrcpy (Memc[tokbuf], Memc[op], SZ_TOKBUF) + } + + Memc[op] = EOS + if (parenlevel > 0) { + call eprintf ("%s: missing right parenthesis in expression\n") + call pargstr (qpexwarn) + status = ERR + } + + # Parse the attribute name to determine the datatype and offset. + + # Get byte offset of field. + ip = atname + 1 + if (ctoi (Memc, ip, boffset) <= 0) + goto badatt_ + + # Get datatype and scaled offset; check field alignment. + switch (Memc[atname]) { + case 'S', 's': + dtype = TY_SHORT + offset = boffset / (SZ_SHORT * SZB_CHAR) + if (offset * SZ_SHORT * SZB_CHAR != boffset) + goto badatt_ + case 'I', 'i': + dtype = TY_INT + offset = boffset / (SZ_INT * SZB_CHAR) + if (offset * SZ_INT * SZB_CHAR != boffset) + goto badatt_ + case 'R', 'r': + dtype = TY_REAL + offset = boffset / (SZ_REAL * SZB_CHAR) + if (offset * SZ_REAL * SZB_CHAR != boffset) + goto badatt_ + case 'D', 'd': + dtype = TY_DOUBLE + offset = boffset / (SZ_DOUBLE * SZB_CHAR) + if (offset * SZ_DOUBLE * SZB_CHAR != boffset) + goto badatt_ + default: + goto badatt_ + } + + # Verify that the field is in range in the event struct. + # (Actually, we don't know the event struct at compile time...) + + max_offset = (boffset / SZB_CHAR) + sizeof(dtype) - 1 + if (boffset < 0 || max_offset > ARB) { +badatt_ call eprintf ("%s: bad attribute name `%s'\n") + call pargstr (qpexwarn) + call pargstr (Memc[atname]) + status = ERR + next + } + + # Clobber any old expression for the given attribute if replace + # mode is in effect. Only previous expression terms are affected, + # hence in single expressions like "pha=x,pha=y", the second entry + # does not clobber the first. + + if (replace) + call qpex_delete (ex, et_tail, offset, dtype) + + # Compile the expression. + switch (dtype) { + case TY_SHORT, TY_INT: + if (qpex_codegeni (ex, Memc[atname], Memc[assignop], + Memc[expr], offset, dtype) == ERR) + status = ERR + case TY_REAL: + if (qpex_codegenr (ex, Memc[atname], Memc[assignop], + Memc[expr], offset, dtype) == ERR) + status = ERR + case TY_DOUBLE: + if (qpex_codegend (ex, Memc[atname], Memc[assignop], + Memc[expr], offset, dtype) == ERR) + status = ERR + } + } + + call qp_closetext (in) + call mfree (expr, TY_CHAR) + call sfree (sp) + + return (status) +end diff --git a/sys/qpoe/qpexopen.x b/sys/qpoe/qpexopen.x new file mode 100644 index 00000000..833a1461 --- /dev/null +++ b/sys/qpoe/qpexopen.x @@ -0,0 +1,67 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <mach.h> +include "qpoe.h" +include "qpex.h" + +# QPEX_OPEN -- Open the expression evaluator. If an expression is given it +# is compiled into the descriptor, otherwise a null (pass all) expression +# is compiled. The compiled expression may be modified or read out at any +# time via calls to other routines in the QPEX package (e.g., qpex_modfilter, +# qpex_getfilter). + +pointer procedure qpex_open (qp, expr) + +pointer qp #I QPOE descriptor +char expr[ARB] #I selection expression (filter) + +pointer ex, pb, db +int pb_len, db_len +int qpex_modfilter() +errchk calloc + +begin + # Allocate the main QPEX descriptor. + call calloc (ex, LEN_EXDES, TY_STRUCT) + + # Allocate the program buffer. + pb_len = QP_EXPBLEN(qp) + call calloc (pb, pb_len, TY_INT) + + # Allocate the data buffer. + db_len = QP_EXDBLEN(qp) + call calloc (db, db_len, TY_CHAR) + + # Initialize the descriptor. + + EX_QP(ex) = qp + EX_DEBUG(ex) = QP_DEBUG(qp) + EX_START(ex) = pb + + EX_PB(ex) = pb + EX_PBTOP(ex) = pb + pb_len + EX_PBOP(ex) = pb + + EX_DB(ex) = db + EX_DBTOP(ex) = db + db_len + EX_DBOP(ex) = db + + EX_MAXFRLUTLEN(ex) = QP_EXMAXFRLLEN(qp) + EX_MAXRRLUTLEN(ex) = QP_EXMAXRRLLEN(qp) + EX_LUTMINRANGES(ex) = QP_EXLMINRANGES(qp) + EX_LUTSCALE(ex) = QP_EXLSCALE(qp) + + if (EX_DEBUG(ex) > 1) { + call eprintf ("QPEX activated, expr = `%s'\n") + call pargstr (expr) + } + + # If a selection expression was given, compile it into the descriptor. + if (qpex_modfilter (ex, expr) == ERR) { + call qpex_close (ex) + call syserrs (SYS_QPEXSYN, QP_DFNAME(qp)) + } + + return (ex) +end diff --git a/sys/qpoe/qpexpand.x b/sys/qpoe/qpexpand.x new file mode 100644 index 00000000..9be19a36 --- /dev/null +++ b/sys/qpoe/qpexpand.x @@ -0,0 +1,60 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_EXPANDTEXT -- Copy a statement to the output, breaking it up into tokens +# and expanding any macro references in the process. This is used to resolve +# macro references which might otherwise be repeatedly expanded, or which it +# might not be possible to expand if this is left to some future time when +# the referenced macros are no longer defined. + +int procedure qp_expandtext (qp, s1, s2, maxch) + +pointer qp #I QPOE descriptor +char s1[ARB] #I input string containing macros +char s2[maxch] #O output string buffer +int maxch #I max chars out + +pointer sp, tokbuf, in +int token, op, otop +int gstrcpy(), qp_gettok() +pointer qp_opentext() + +begin + call smark (sp) + call salloc (tokbuf, SZ_TOKBUF, TY_CHAR) + + # Open input text for macro expanded token input. + in = qp_opentext (qp, s1) + otop = maxch + 1 + op = 1 + + # Copy tokens to the output, inserting a space after every token. + repeat { + token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) + if (token != EOF) { + if (token == TOK_STRING) { + s2[op] = '"' + op = min (otop, op + 1) + } + op = op + gstrcpy (Memc[tokbuf], s2[op], otop-op) + if (token == TOK_STRING) { + s2[op] = '"' + op = min (otop, op + 1) + } + s2[op] = ' '; op = min (otop, op + 1) + if (op >= otop) + break + } + } until (token == EOF) + + # Cancel the trailing blank and add the EOS. + if (op > 1 && op < otop) + op = op - 1 + s2[op] = EOS + + call qp_closetext (in) + call sfree (sp) + + return (op - 1) +end diff --git a/sys/qpoe/qpexparse.gx b/sys/qpoe/qpexparse.gx new file mode 100644 index 00000000..c6f40042 --- /dev/null +++ b/sys/qpoe/qpexparse.gx @@ -0,0 +1,410 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include <mach.h> +include "../qpex.h" + +.help qpexparse +.nf -------------------------------------------------------------------------- +QPEXPARSE -- Code to parse an event attribute expression, producing a binary +range list as output. + + nranges = qpex_parse[ird] (expr, xs, xe, xlen) + +The calling sequence for the parse routine is shown above. The arguments XS +and XE are pointers to dynamically allocated arrays of length XLEN and type +[IRD]. These arrays should be allocated in the calling program before calling +the parser, and deallocated when no longer needed. Reallocation to increase +the array length is automatic if the arrays fill during parsing. DTYPE should +be the same datatype as the attribute with which the list is associated. + +The form of an event attribute expression may be a list of values, + + attribute = n +or + attribute = m, n, ... + +a list of inclusive or exclusive ranges, + + attribute = m:n, !p:q + +including open ranges, + + attribute = :n, p:q + +or any combination of the above (excluding combinations of bitmasks and values +or ranges, which are mutually exclusive): + + attribute = :n, a, b, p:q, !(m, e:f) + +Parenthesis may be used for grouping where desired, e.g., + + attribute = (:n, a, b, p:q, !(m, e:f)) + +An additional form of the event attribute expression allows use of a bitmask +to specify the acceptable values, e.g., + + attribute = %17B +or + attribute = !%17B + +however, bitmasks are incompatible with range lists, and should be recognized +and dealt with elsewhere (bitmasks may not be combined with range lists in +the same expression term). + +We are concerned here only with the attribute value list itself, i.e., +everything to the right of the equals sign in the examples above. This list +should be extracted and placed into a string containing a single line of +text before we are called. Attribute value lists may be any length, but +backslash continuation, file inclusion (or whatever means is used to form +the attribute value list) is assumed to be handled at a higher level. + +The output of this package is an ordered boolean valued binary range list +with type integer, real, or double breakpoints (i.e., the breakpoints are the +same datatype as the attribute itself, but the range values are zero or one). +The range list defines the initial value, final value, and any interior +breakpoints where the attribute value changes state. Expression optimization +is used to minimize the number of breakpoints (i.e., eliminate redundant +breakpoints, such as a range within a range). + +Output range list format: + + xs[1] xe[1] + xs[2] xe[2] + ... + xs[N] xe[N] + +Where each range is inclusive and only "true" ranges are shown. If XS[1] is +LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right +range (n:) is indicated. In an integer range list, isolated points appear +as a single range with (xe[i]=xs[i]). In a real or double range list, +isolated points are represented as finite ranges with a width on the order of +the machine epsilon. +.endhelp --------------------------------------------------------------------- + +define DEF_XLEN 256 # default output range list length +define INC_XLEN 256 # increment to above +define DEF_VLEN 512 # default breakpoint list length +define INC_VLEN 512 # increment to above +define MAX_NEST 20 # parser stack depth + +define STEP 1 # step at boundary of closed range +define ZERO 1000 # step at boundary of open range + +define XV Mem$t[xv+($1)-1] # reference x position values +define UV Memi[uv+($1)-1] # unique flags for x value pairs +define SV Memi[sv+($1)-1] # reference breakpoint step values + + +# QPEX_PARSE -- Convert the given attribute value list into a binary +# range list, returning the number of ranges as the function value. + +int procedure qpex_parse$t (expr, xs, xe, xlen) + +char expr[ARB] #I attribute value list to be parsed +pointer xs #U pointer to array of start-range values +pointer xe #U pointer to array of end-range values +int xlen #U allocated length of XS, XE arrays + +bool range +pointer xv, uv, sv +PIXEL xstart, xend, xmin, temp, x, n_xs, n_xe +int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy +int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin +errchk syserr, malloc, realloc +define pop_ 91 + +$if (datatype == si) +int qp_ctoi() +define fp_equal$t($1==$2) +$else +double dtemp +bool bval, fp_equal$t() +int qp_ctod() +$endif + +begin + vlen = DEF_VLEN + call malloc (xv, vlen, TY_PIXEL) + call malloc (uv, vlen, TY_INT) + call malloc (sv, vlen, TY_INT) + + lev = 0 + nrg = 0 + + # Parse the expression string and compile the raw, unoptimized + # breakpoint list in the order in which the breakpoints occur in + # the string. + + for (ip=1; expr[ip] != EOS; ) { + # Skip whitespace. + for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip]) + ip = ip + 1 + + # Extract and process token. + switch (ch) { + case EOS: + # At end of string. + if (lev > 0) + goto pop_ + else + break + + case ',': + # Comma list token delmiter. + ip = ip + 1 + goto pop_ + + case '!', '(': + # Syntactical element - push on stack. + ip = ip + 1 + lev = lev + 1 + if (lev > MAX_NEST) + call syserr (SYS_QPEXLEVEL) + token[lev] = ch + tokval[lev] = nrg + 1 + + case ')': + # Close parenthesized group and pop parser stack. + ip = ip + 1 + if (lev < 1) + call syserr (SYS_QPEXMLP) + else if (token[lev] != '(') + call syserr (SYS_QPEXRPAREN) + lev = lev - 1 + goto pop_ + + default: + # Process a range term. + ip_start = ip + + # Scan the M in M:N. + $if (datatype == si) + if (qp_ctoi (expr, ip, xstart) <= 0) + xstart = LEFT$T + $else + if (qp_ctod (expr, ip, dtemp) <= 0) + xstart = LEFT$T + else + xstart = dtemp + $endif + + # Scan the : in M:N. The notation M-N is also accepted, + # provided the token - immediately follows the token M. + + while (IS_WHITE(expr[ip])) + ip = ip + 1 + range = (expr[ip] == ':') + if (range) + ip = ip + 1 + else if (!IS_LEFT$T (xstart)) { + range = (expr[ip] == '-') + if (range) + ip = ip + 1 + } + + # Scan the N in M:N. + if (range) { + $if (datatype == si) + if (qp_ctoi (expr, ip, xend) <= 0) + xend = RIGHT$T + $else + if (qp_ctod (expr, ip, dtemp) <= 0) + xend = RIGHT$T + else + xend = dtemp + $endif + } else + xend = xstart + + # Fix things if the user entered M:M explicitly. + if (range) + if (fp_equal$t (xstart, xend)) + range = false + + # Expand a single point into a range. For an integer list + # this produces M:M+1; for a floating list M-eps:M+eps. + # Verify ordering and that something recognizable was scanned. + + if (!range) { + if (IS_LEFT$T(xstart)) + call syserr (SYS_QPEXBADRNG) + $if (datatype == si) + xend = xstart + 1 + $endif + } else { + if (xstart > xend) { + temp = xstart; xstart = xend; xend = temp + } + $if (datatype == si) + if (!IS_RIGHT$T(xend)) + xend = xend + 1 + $endif + } + + # Make more space if vectors fill up. + if (nrg+4 > vlen) { + vlen = vlen + INC_VLEN + call realloc (xv, vlen, TY_PIXEL) + call realloc (uv, vlen, TY_INT) + call realloc (sv, vlen, TY_INT) + } + + # Save range on intermediate breakpoint list. + nrg = nrg + 1 + XV(nrg) = xstart + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = xend + UV(nrg) = 1 + SV(nrg) = -STEP +pop_ + # Pop parser stack. + if (lev > 0) + if (token[lev] == '!') { + # Invert a series of breakpoints. + do i = tokval[lev], nrg { + if (SV(i) == STEP) # invert + SV(i) = -ZERO + else if (SV(i) == -STEP) + SV(i) = ZERO + else if (SV(i) == ZERO) # undo + SV(i) = -STEP + else if (SV(i) == -ZERO) + SV(i) = STEP + } + lev = lev - 1 + } + } + } + + # If the first range entered by the user is an exclude range, + # e.g., "(!N)" or "(!(expr))" this implies that all other values + # are acceptable. Add the open range ":" to the end of the range + # list to indicate this, i.e., convert "!N" to ":,!N". + + if (SV(1) == -ZERO) { + nrg = nrg + 1 + XV(nrg) = LEFT$T + UV(nrg) = 0 + SV(nrg) = STEP + + nrg = nrg + 1 + XV(nrg) = RIGHT$T + UV(nrg) = 1 + SV(nrg) = -STEP + } + + # Sort the breakpoint list. + do j = 1, nrg { + xmin = XV(j); umin = UV(j) + jval = j + do i = j+1, nrg { + $if (datatype == rd) + bval = (XV(i) < xmin) + if (!bval) + if (abs (XV(i) - xmin) < 1.0E-5) + bval = (fp_equal$t(XV(i),xmin) && UV(i) < umin) + if (bval) { + $else + if (XV(i) < xmin || (XV(i) == xmin && UV(i) < umin)) { + $endif + xmin = XV(i); umin = UV(i) + jval = i + } + } + if (jval != j) { + temp = XV(j); XV(j) = XV(jval); XV(jval) = temp + itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp + itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp + } + } + + # Initialize the output arrays if they were passed in as null. + if (xlen <= 0) { + xlen = DEF_XLEN + call malloc (xs, xlen, TY_PIXEL) + call malloc (xe, xlen, TY_PIXEL) + } + + # Collapse sequences of redundant breakpoints into a single + # breakpoint, clipping the running sum value to the range 0-1. + # Accumulate and output successive nonzero ranges. + + op = 1 + ov = 0 + y = 0 + + for (r1=1; r1 <= nrg; r1=r2+1) { + # Get a range of breakpoint entries for a single XV position. + for (r2=r1; r2 <= nrg; r2=r2+1) { + $if (datatype == si) + if (XV(r2) != XV(r1)) + break + $else + bval = (UV(r2) != UV(r1)) + if (!bval) { + bval = (abs (XV(r2) - XV(r1)) > 1.0E-5) + if (!bval) + bval = !fp_equal$t(XV(r2),XV(r1)) + } + if (bval) + break + $endif + } + r2 = r2 - 1 + + # Collapse into a single breakpoint. + x = XV(r1) + dy = SV(r1) + do i = r1 + 1, r2 + dy = dy + SV(i) + y = y + dy + + # Clip value to the range 0-1. + v = max(0, min(1, y)) + + # Accumulate a range of nonzero value. This eliminates redundant + # points lying within a range which is already set high. + + if (v == 1 && ov == 0) { + n_xs = x + ov = 1 + } else if (v == 0 && ov == 1) { + $if (datatype == si) + if (IS_RIGHT$T(x)) + n_xe = x + else + n_xe = x - 1 + $else + n_xe = x + $endif + ov = 2 + } + + # Output a range. + if (ov == 2) { + if (op > xlen) { + xlen = xlen + INC_XLEN + call realloc (xs, xlen, TY_PIXEL) + call realloc (xe, xlen, TY_PIXEL) + } + + Mem$t[xs+op-1] = n_xs + Mem$t[xe+op-1] = n_xe + op = op + 1 + + ov = 0 + } + } + + # All done; discard breakpoint buffers. + call mfree (xv, TY_PIXEL) + call mfree (uv, TY_INT) + call mfree (sv, TY_INT) + + return (op - 1) +end diff --git a/sys/qpoe/qpexsub.gx b/sys/qpoe/qpexsub.gx new file mode 100644 index 00000000..1fc51821 --- /dev/null +++ b/sys/qpoe/qpexsub.gx @@ -0,0 +1,67 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a +# larger range list. The number of ranges extracted is returned as the +# function value. + +int procedure qpex_sublist$t (x1, x2, xs,xe,nranges,ip, o_xs,o_xe) + +PIXEL x1, x2 #I range to be extracted +PIXEL xs[nranges],xe[nranges] #I input range list +int nranges #I nranges in input list +int ip #U start position in input list +PIXEL o_xs[ARB],o_xe[ARB] #O output sublist + +PIXEL tol +int op, i + +begin + $if (datatype == i) + tol = 0 + $else + tol = (EPSILON$T * 10$f) + $endif + + # Determine the range containing or immediately following the + # start point of the range of interest. + + while (x1 < xs[ip] && ip > 1) + ip = ip - 1 + while (x1 >= xs[ip]) + if (x1 <= xe[ip] || ip >= nranges) + break + else + ip = ip + 1 + + # Check for an empty output range list. + if (xs[ip] > x2) + return (0) + + # At least one input range contributes something to the output region. + # Copy a portion of the input range list to the ouput range list. + + op = 1 + do i = ip, nranges { + if (xs[i] <= x1) + o_xs[op] = LEFT$T - tol + else + o_xs[op] = xs[i] + + if ((xe[i] - x2) >= tol) { + o_xe[op] = RIGHT$T + tol + op = op + 1 + break + } else + o_xe[op] = xe[i] + + op = op + 1 + if (xs[i+1] > x2) + break + } + + ip = i + return (op - 1) +end diff --git a/sys/qpoe/qpget.gx b/sys/qpoe/qpget.gx new file mode 100644 index 00000000..dfdca1b0 --- /dev/null +++ b/sys/qpoe/qpget.gx @@ -0,0 +1,67 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_GET -- Return the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# returned by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +PIXEL procedure qp_get$t (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int dtype +PIXEL value +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + dtype = qp_getparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Set default value of INDEF or NULL. + $if (datatype == c) + value = (NULL) + $else + value = (INDEF) + $endif + + # Get a valid parameter value. + switch (dtype) { + case TY_CHAR: + value = (Memc[pp]) + case TY_SHORT: + if (!IS_INDEFS(Mems[pp])) + value = (Mems[pp]) + case TY_INT: + if (!IS_INDEFI(Memi[pp])) + value = (Memi[pp]) + case TY_LONG: + if (!IS_INDEFL(Meml[pp])) + value = (Meml[pp]) + case TY_REAL: + if (!IS_INDEFR(Memr[pp])) + value = (Memr[pp]) + case TY_DOUBLE: + if (!IS_INDEFD(Memd[pp])) + value = (Memd[pp]) + default: + call syserrs (SYS_QPBADCONV, param) + } + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n") + call pargstr (param) + call pargi (dtype) + call pargi (TY_PIXEL) + call parg$t (value) + } + + return (value) +end diff --git a/sys/qpoe/qpgetb.x b/sys/qpoe/qpgetb.x new file mode 100644 index 00000000..cea5c5be --- /dev/null +++ b/sys/qpoe/qpgetb.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_GETB -- Return the boolean value of the named header parameter. Type +# conversion is not permitted between boolean and the other datatypes. + +bool procedure qp_getb (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + if (qp_getparam (qp, param, pp) != TY_BOOL) + call syserrs (SYS_QPBADCONV, param) + else if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + return (Memb[pp]) +end diff --git a/sys/qpoe/qpgettok.x b/sys/qpoe/qpgettok.x new file mode 100644 index 00000000..feb8d780 --- /dev/null +++ b/sys/qpoe/qpgettok.x @@ -0,0 +1,687 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <error.h> +include <ctype.h> +include <fset.h> +include "qpoe.h" + +.help gettok +.nf -------------------------------------------------------------------------- +GETTOK -- Lexical input routines for QPOE. Used to return tokens from input +text; this is where all macro expansion and file expansion takes place. + + gt = qp_opentext (qp, text) + token = qp_gettok (gt, tokbuf, maxch) + qp_ungettok (gt, tokbuf) + token = qp_rawtok (gt, tokbuf, maxch) + token = qp_nexttok (gt) + qp_closetext (gt) + +Access to the package is gained by opening a text string with QP_OPENTEXT. +This returns a descriptor which is passed to QP_GETTOK to read successive +tokens, which may come from the input text string or from any macros, +include files, etc., referenced in the text or in any substituted text. +QP_UNGETTOK pushes a token back into the QP_GETTOK input stream, to be +returned in the next QP_GETTOK call (following macro expansion). + +QP_RAWTOK returns the next physical token from an input stream (without +macro expansion), and QP_NEXTTOK returns the type of the next *physical* +token (no macro expansion) without actually fetching it (for look ahead +decision making). + +The tokens that can be returned are as follows: + + TOK_IDENTIFIER [a-zA-Z][a-zA-Z0-9_]* + TOK_NUMBER [0-9][0-9a-zA-Z.]*(e|E)?(+|-)?[0-9]* + TOK_STRING if "abc" or 'abc', the abc + 'c' other characters, e.g., =+-*/,;:()[] etc + EOF at end of input + +Macro replacement syntax: + + macro push macro with null arglist + macro(arg,arg,...) push macro with argument substitution + @file push contents of file + @file(arg,arg,...) push file with argument substitution + `cmd` substitute output of CL command "cmd" + +where + macro is an identifier, the name of a global macro + or a datafile local macro (parameter) + +In all cases, occurences of $N in the replacement text are replaced by the +macro arguments if any, and macros are recursively expanded. Whitespace, +including newline, equates to a single space, as does EOF (hence always +delimits tokens). Comments (# to end of line) are ignored. All identifiers +in scanned text are checked to see if they are references to predefined global +or local (datafile) macros. + +A global macro is a symbol defined globally for QPOE, in effect for all poefile +accesses (see qpmacro.x). A local macro is a macro defined as a string +parameter of type TY_MACRO in the poefile header (and hence affecting only +that one datafile). +.endhelp --------------------------------------------------------------------- + +# General definitions. +define MAX_LEVELS 20 # max include file nesting +define MAX_ARGS 9 # max arguments to a macro +define SZ_CMD 80 # `cmd` +define SZ_IBUF 8192 # buffer for macro replacement +define SZ_OBUF 8192 # buffer for macro replacement +define SZ_ARGBUF 256 # argument list to a macro + +# The gettok descriptor. +define LEN_GTDES 45 +define GT_QP Memi[$1] # backpointer to QPOE descriptor +define GT_FD Memi[$1+1] # current input stream +define GT_NEXTCH Memi[$1+2] # lookahead character +define GT_FTEMP Memi[$1+3] # file on stream is a temp file +define GT_LEVEL Memi[$1+4] # current nesting level +define GT_SVFD Memi[$1+5+$2-1] # stacked file descriptors +define GT_SVFTEMP Memi[$1+25+$2-1]# stacked ftemp flags + + +# QP_OPENTEXT -- Open the QP_GETTOK descriptor. The descriptor is initially +# opened on the user supplied string buffer (which is opened as a file and +# which must remain intact while token input is in progress), but include file +# processing etc. may cause arbitrary nesting of file descriptors. + +pointer procedure qp_opentext (qp, text) + +pointer qp #I QPOE descriptor +char text[ARB] #I input text to be scanned + +pointer gt +int sz_pbbuf +int stropen(), strlen() +errchk stropen, calloc + +begin + call calloc (gt, LEN_GTDES, TY_STRUCT) + + GT_QP(gt) = qp + GT_FD(gt) = stropen (text, strlen(text), READ_ONLY) + + if (qp == NULL) + sz_pbbuf = DEF_MAXPUSHBACK + else + sz_pbbuf = QP_SZPBBUF(qp) + call fseti (GT_FD(gt), F_PBBSIZE, sz_pbbuf) + + return (gt) +end + + +# QP_GETTOK -- Return the next token from the input stream. The token ID +# (a predefined integer code or the character value) is returned as the +# function value. The text of the token is returned as an output argument. +# Any macro references, file includes, etc., are performed in the process +# of scanning the input stream, hence only fully resolved tokens are output. + +int procedure qp_gettok (gt, tokbuf, maxch) + +pointer gt #I gettok descriptor +char tokbuf[maxch] #O receives the text of the token +int maxch #I max chars out + +pointer sp, bp, qp, cmd, ibuf, obuf, argbuf, fname, sym, textp +int fd, token, level, nargs, nchars, i_fd, o_fd, ftemp + +bool streq() +pointer qp_gmsym() +int strmac(), open(), stropen() +int qp_rawtok(), qp_nexttok(), qp_arglist() +errchk qp_rawtok,close,ungetci,ungetline,qp_arglist,clcmdw,stropen,syserr +define pushfile_ 91 + + +begin + call smark (sp) + + # Allocate some buffer space. + nchars = SZ_CMD + SZ_IBUF + SZ_OBUF + SZ_ARGBUF + SZ_FNAME + 5 + call salloc (bp, nchars, TY_CHAR) + + cmd = bp + ibuf = cmd + SZ_CMD + 1 + obuf = ibuf + SZ_IBUF + 1 + argbuf = obuf + SZ_OBUF + 1 + fname = argbuf + SZ_ARGBUF + 1 + + qp = GT_QP(gt) + + # Read raw tokens and push back macro or include file text until we + # get a fully resolved token. + + repeat { + fd = GT_FD(gt) + + # Get a raw token. + token = qp_rawtok (gt, tokbuf, maxch) + + # Process special tokens. + switch (token) { + case EOF: + # EOF has been reached on the current stream. + level = GT_LEVEL(gt) + if (GT_FTEMP(gt) == YES) { + call fstats (fd, F_FILENAME, Memc[fname], SZ_FNAME) + if (level > 0) + call close (fd) + iferr (call delete (Memc[fname])) + call erract (EA_WARN) + } else if (level > 0) + call close (fd) + + if (level > 0) { + # Restore previous stream. + GT_FD(gt) = GT_SVFD(gt,level) + GT_FTEMP(gt) = GT_SVFTEMP(gt,level) + GT_LEVEL(gt) = level - 1 + GT_NEXTCH(gt) = NULL + } else { + # Return EOF token to caller. + call strcpy ("EOF", tokbuf, maxch) + break + } + + case TOK_IDENTIFIER: + # Lookup the identifier in the symbol table. + sym = NULL + if (qp != NULL) + sym = qp_gmsym (qp, tokbuf, textp) + + # Process a defined macro. + if (sym != NULL) { + # If macro does not have any arguments, merely push back + # the replacement text. + + if (and (S_FLAGS(sym), SF_MACARGS) == 0) { + if (GT_NEXTCH(gt) > 0) { + call ungetci (fd, GT_NEXTCH(gt)) + GT_NEXTCH(gt) = 0 + } + call ungetline (fd, Memc[textp]) + next + } + + # Extract argument list, if any, perform argument + # substitution on the macro, and push back the edited + # text to be rescanned. + + if (qp_nexttok(gt) == '(') { + nargs = qp_arglist (gt, Memc[argbuf], SZ_ARGBUF) + + # Pushback the text of a macro with arg substitution. + nchars = strmac (Memc[textp], Memc[argbuf], + Memc[obuf], SZ_OBUF) + if (GT_NEXTCH(gt) > 0) { + call ungetci (fd, GT_NEXTCH(gt)) + GT_NEXTCH(gt) = 0 + } + call ungetline (fd, Memc[obuf]) + next + + } else { + call eprintf ("macro `%s' called with no arguments\n") + call pargstr (tokbuf) + } + } + + # Check for the builtin symbol $DFN, the datafile name. + if (tokbuf[1] == '$') { + if (streq (tokbuf, "$DFN")) { + call strcpy (QP_DFNAME(qp), tokbuf, maxch) + token = TOK_STRING + break + } + } + + # Return a regular identifier. + break + + case TOK_COMMAND: + # Send a command to the CL and push back the output. + + # Execute the command, spooling the output in a temp file. + call mktemp ("tmp$co", Memc[fname], SZ_FNAME) + call sprintf (Memc[cmd], SZ_LINE, "%s > %s") + call pargstr (tokbuf) + call pargstr (Memc[fname]) + call clcmdw (Memc[cmd]) + + # Open the output file as input text. + call strcpy (Memc[fname], tokbuf, maxch) + nargs = 0 + ftemp = YES + goto pushfile_ + + case '@': + token = qp_rawtok (gt, tokbuf, maxch) + if (token != TOK_IDENTIFIER && token != TOK_STRING) { + call eprintf ("expected a filename after the `@'\n") + next + } else { + nargs = 0 + if (qp_nexttok(gt) == '(') # ) + nargs = qp_arglist (gt, Memc[argbuf], SZ_ARGBUF) + ftemp = NO + } +pushfile_ + # Attempt to open the file; first try the given name, then + # if that doesn't work, try adding the macro file extension. + + iferr (i_fd = open (tokbuf, READ_ONLY, TEXT_FILE)) { + call qp_mkfname (tokbuf, + QPOE_MACROEXTN, Memc[fname], SZ_FNAME) + iferr (i_fd = open (Memc[fname],READ_ONLY,TEXT_FILE)) { + call eprintf ("cannot open `%s'\n") + call pargstr (tokbuf) + next + } + } + + if (qp != NULL) + call fseti (i_fd, F_PBBSIZE, QP_SZPBBUF(qp)) + else + call fseti (i_fd, F_PBBSIZE, DEF_MAXPUSHBACK) + + # Cancel lookahead. + if (GT_NEXTCH(gt) > 0) { + call ungetci (fd, GT_NEXTCH(gt)) + GT_NEXTCH(gt) = 0 + } + + # If the macro was called with a nonnull argument list, + # attempt to perform argument substitution on the file + # contents. Otherwise merely push the fd. + + if (nargs > 0) { + # Pushback file contents with argument substitution. + o_fd = stropen (Memc[ibuf], SZ_IBUF, NEW_FILE) + + call fcopyo (i_fd, o_fd) + nchars = strmac (Memc[ibuf],Memc[argbuf],Memc[obuf],SZ_OBUF) + call ungetline (fd, Memc[obuf]) + + call close (o_fd) + call close (i_fd) + + } else { + # Push a new input stream. + level = GT_LEVEL(gt) + 1 + if (level > MAX_LEVELS) + call syserr (SYS_QPMRECUR) + + GT_SVFD(gt,level) = GT_FD(gt) + GT_SVFTEMP(gt,level) = GT_FTEMP(gt) + GT_LEVEL(gt) = level + + fd = i_fd + GT_FD(gt) = fd + GT_FTEMP(gt) = ftemp + } + + default: + break + } + } + + if (qp != NULL) + if (QP_DEBUG(qp) > 4) { + call eprintf ("token=%d(%o), `%s'\n") + call pargi (token) + call pargi (max(0,token)) + if (IS_PRINT(tokbuf[1])) + call pargstr (tokbuf) + else + call pargstr ("") + } + + call sfree (sp) + return (token) +end + + +# QP_UNGETTOK -- Push a token back into the QP_GETTOK input stream, to be +# returned as the next token by QP_GETTOK. + +procedure qp_ungettok (gt, tokbuf) + +pointer gt #I gettok descriptor +char tokbuf[ARB] #I text of token + +int fd +pointer qp +errchk ungetci + +begin + fd = GT_FD(gt) + qp = GT_QP(gt) + + if (qp != NULL) + if (QP_DEBUG(qp) > 4) { + call eprintf ("unget token `%s'\n") + call pargstr (tokbuf) + } + + # Cancel lookahead. + if (GT_NEXTCH(gt) > 0) { + call ungetci (fd, GT_NEXTCH(gt)) + GT_NEXTCH(gt) = 0 + } + + # First push back a space to ensure that the token is recognized + # when the input is rescanned. + + call ungetci (fd, ' ') + + # Now push the token text. + call ungetline (fd, tokbuf) +end + + +# QP_RAWTOK -- Get a raw token from the input stream, without performing any +# macro expansion or file inclusion. The text of the token in returned in +# tokbuf, and the token type is returened as the function value. + +int procedure qp_rawtok (gt, outstr, maxch) + +pointer gt #I gettok descriptor +char outstr[maxch] #O receives text of token. +int maxch #I max chars out + +int token, delim, fd, ch, op +define again_ 91 +int getci() + +begin + fd = GT_FD(gt) +again_ + # Get lookahead char if we don't already have one. + ch = GT_NEXTCH(gt) + GT_NEXTCH(gt) = NULL + if (ch <= 0 || IS_WHITE(ch) || ch == '\n') { + while (getci (fd, ch) != EOF) + if (!(IS_WHITE(ch) || ch == '\n')) + break + } + + # Output the first character. + op = 1 + if (ch != EOF && ch != '"' && ch != '\'' && ch != '`') { + outstr[op] = ch + op = op + 1 + } + + # Accumulate token. Some of the token recognition logic used here + # (especially for numbers) is crude, but it is not clear that rigour + # is justified for this application. + + if (ch == EOF) { + call strcpy ("EOF", outstr, maxch) + token = EOF + + } else if (ch == '#') { + # Ignore a comment. + while (getci (fd, ch) != '\n') + if (ch == EOF) + break + goto again_ + + } else if (IS_ALPHA(ch) || ch == '_' || ch == '$' || ch == '.') { + # Identifier. + token = TOK_IDENTIFIER + while (getci (fd, ch) != EOF) + if (IS_ALNUM(ch) || ch == '_' || ch == '$' || ch == '.') { + outstr[op] = ch + op = min (maxch, op+1) + } else + break + + } else if (IS_DIGIT(ch)) { + # Number. + token = TOK_NUMBER + + # Get number. + while (getci (fd, ch) != EOF) + if (IS_ALNUM(ch) || ch == '.') { + outstr[op] = ch + op = min (maxch, op+1) + } else + break + + # Get exponent if any. + if (ch == 'E' || ch == 'e') { + outstr[op] = ch + op = min (maxch, op+1) + while (getci (fd, ch) != EOF) + if (IS_DIGIT(ch) || ch == '+' || ch == '-') { + outstr[op] = ch + op = min (maxch, op+1) + } else + break + } + + } else if (ch == '"' || ch == '\'' || ch == '`') { + # Quoted string or command. + + if (ch == '`') + token = TOK_COMMAND + else + token = TOK_STRING + + delim = ch + while (getci (fd, ch) != EOF) + if (ch==delim && (op>1 && outstr[op-1] != '\\') || ch == '\n') + break + else { + outstr[op] = ch + op = min (maxch, op+1) + } + ch = getci (fd, ch) + + } else if (ch == '+') { + # May be the += operator. + if (getci (fd, ch) != EOF) + if (ch == '=') { + token = TOK_PLUSEQUALS + outstr[op] = ch + op = op + 1 + ch = getci (fd, ch) + } else + token = '+' + + } else if (ch == ':') { + # May be the := operator. + if (getci (fd, ch) != EOF) + if (ch == '=') { + token = TOK_COLONEQUALS + outstr[op] = ch + op = op + 1 + ch = getci (fd, ch) + } else + token = ':' + + } else { + # Other characters. + token = ch + ch = getci (fd, ch) + } + + # Process the lookahead character. + if (IS_WHITE(ch) || ch == '\n') { + repeat { + ch = getci (fd, ch) + } until (!(IS_WHITE(ch) || ch == '\n')) + } + + if (ch != EOF) + GT_NEXTCH(gt) = ch + + outstr[op] = EOS + return (token) +end + + +# QP_NEXTTOK -- Determine the type of the next raw token in the input stream, +# without actually fetching the token. TOK_PLUSEQUALS is not recognized at +# this level. Note that this is at the same level as QP_RAWTOK, i.e., no +# macro expansion is performed, and the lookahead token is that which would +# be returned by the next qp_rawtok, which is not necessarily what qp_gettok +# would return after macro replacement. + +int procedure qp_nexttok (gt) + +pointer gt #I gettok descriptor + +pointer qp +int token, fd, ch +int getci() + +begin + fd = GT_FD(gt) + qp = GT_QP(gt) + + # Get lookahead char if we don't already have one. + ch = GT_NEXTCH(gt) + if (ch <= 0 || IS_WHITE(ch) || ch == '\n') + while (getci (fd, ch) != EOF) + if (!(IS_WHITE(ch) || ch == '\n')) + break + + if (ch == EOF) + token = EOF + else if (IS_ALPHA(ch)) + token = TOK_IDENTIFIER + else if (IS_DIGIT(ch)) + token = TOK_NUMBER + else if (ch == '"' || ch == '\'') + token = TOK_STRING + else if (ch == '`') + token = TOK_COMMAND + else + token = ch + + if (qp != NULL) + if (QP_DEBUG(qp) > 4) { + call eprintf ("nexttok=%d(%o) `%c'\n") + call pargi (token) + call pargi (max(0,token)) + if (IS_PRINT(ch)) + call pargi (ch) + else + call pargi (0) + } + + return (token) +end + + +# QP_CLOSETEXT -- Close the gettok descriptor and any files opened thereon. + +procedure qp_closetext (gt) + +pointer gt #I gettok descriptor + +int level, fd +pointer sp, fname + +begin + call smark (sp) + call salloc (fname, SZ_FNAME, TY_CHAR) + + for (level=GT_LEVEL(gt); level >= 0; level=level-1) { + fd = GT_FD(gt) + if (GT_FTEMP(gt) == YES) { + call fstats (fd, F_FILENAME, Memc[fname], SZ_FNAME) + call close (fd) + iferr (call delete (Memc[fname])) + call erract (EA_WARN) + } else + call close (fd) + + if (level > 0) { + GT_FD(gt) = GT_SVFD(gt,level) + GT_FTEMP(gt) = GT_SVFTEMP(gt,level) + } + } + + call mfree (gt, TY_STRUCT) + call sfree (sp) +end + + +# QP_ARGLIST -- Extract a paren and comma delimited argument list to be used +# for substitution into a macro replacement string. Since the result will be +# pushed back and rescanned, we do not have to perform macro substitution on +# the argument list at this level. + +int procedure qp_arglist (gt, argbuf, maxch) + +pointer gt #I gettok descriptor +char argbuf[maxch] #O receives parsed arguments +int maxch #I max chars out + +int level, quote, nargs, op, ch, fd +int getci() + +begin + fd = GT_FD(gt) + + # Get lookahead char if we don't already have one. + ch = GT_NEXTCH(gt) + if (ch <= 0 || IS_WHITE(ch) || ch == '\n') + while (getci (fd, ch) != EOF) + if (!(IS_WHITE(ch) || ch == '\n')) + break + + quote = 0 + level = 1 + nargs = 0 + op = 1 + + if (ch == '(') { + while (getci (fd, ch) != EOF) { + if (ch == '"' || ch == '\'') { + if (quote == 0) + quote = ch + else if (quote == ch) + quote = 0 + + } else if (ch == '(' && quote == 0) { + level = level + 1 + } else if (ch == ')' && quote == 0) { + level = level - 1 + if (level <= 0) { + if (op > 1 && argbuf[op-1] != EOS) + nargs = nargs + 1 + break + } + + } else if (ch == ',' && level == 1 && quote == 0) { + ch = EOS + nargs = nargs + 1 + } else if (ch == '\n') { + ch = ' ' + } else if (ch == '\\' && quote == 0) { + ch = getci (fd, ch) + next + } else if (ch == '#' && quote == 0) { + while (getci (fd, ch) != EOF) + if (ch == '\n') + break + next + } + + argbuf[op] = ch + op = min (maxch, op + 1) + } + + GT_NEXTCH(gt) = NULL + } + + argbuf[op] = EOS + return (nargs) +end diff --git a/sys/qpoe/qpgetx.x b/sys/qpoe/qpgetx.x new file mode 100644 index 00000000..cf9468bb --- /dev/null +++ b/sys/qpoe/qpgetx.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_GETX -- Return the complex value of the named header parameter. Type +# conversion is not permitted between complex and the other datatypes. + +complex procedure qp_getx (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer pp +int qp_getparam() +errchk qp_getparam, syserrs + +begin + # Lookup the parameter and it's value. + if (qp_getparam (qp, param, pp) != TY_COMPLEX) + call syserrs (SYS_QPBADCONV, param) + else if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + return (Memx[pp]) +end diff --git a/sys/qpoe/qpgmsym.x b/sys/qpoe/qpgmsym.x new file mode 100644 index 00000000..883f5c22 --- /dev/null +++ b/sys/qpoe/qpgmsym.x @@ -0,0 +1,76 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_GMSYM -- Lookup the named macro in the symbol table and return a pointer +# to the symstruct describing the macro as the function value. NULL is +# returned if the macro is not defined, or if the named symbol is not a macro. +# Local macros take precedence over global macros. In the case of a local +# macro whose value is stored in the datafile, we have to allocate an internal +# buffer to hold the data after we exit; this data must be used promptly, +# before the routine is again called. + +pointer procedure qp_gmsym (qp, macro, textp) + +pointer qp #I QPOE descriptor +char macro[ARB] #I macro name +pointer textp #O char pointer to macro text + +int sz_textbuf, nchars, fd +pointer st, sm, sym, textbuf +data textbuf /NULL/, sz_textbuf /NULL/ + +int fm_getfd(), read() +pointer qm_symtab(), strefsbuf(), stfind() +errchk realloc, fm_getfd, seek, read + +begin + st = QP_ST(qp) + sm = qm_symtab (QP_QM(qp)) + + # First look in the datafile local symbol table. Macros are stored + # in the datafile symbol table as string macros of type TY_MACRO. + + sym = stfind (st, macro) + if (sym != NULL) + if (S_DTYPE(sym) == TY_MACRO) + if (and (S_FLAGS(sym), SF_DELETED) == 0) { + if (S_LFILE(sym) > 0) { + # Macro value stored as data. + + # Make sure the text buffer is large enough. + if (sz_textbuf < S_NELEM(sym)) { + sz_textbuf = S_NELEM(sym) + call realloc (textbuf, sz_textbuf, TY_CHAR) + } + + # Read the data. + fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_ONLY, 0) + + call seek (fd, S_OFFSET(sym)) + nchars = max (0, read (fd, Memc[textbuf], S_NELEM(sym))) + Memc[textbuf+nchars] = EOS + textp = textbuf + + call fm_retfd (QP_FM(qp), S_LFILE(sym)) + + } else { + # Macro value stored in symbol table. + textp = strefsbuf (st, S_OFFSET(sym)) + } + + # Exit if a local symbol was found. + return (sym) + } + + # Next look in the global macro symbol table. + sym = stfind (sm, macro) + if (sym != NULL) + if (and (S_FLAGS(sym), SF_DELETED) == 0) + textp = strefsbuf (sm, S_OFFSET(sym)) + else + sym = NULL + + return (sym) +end diff --git a/sys/qpoe/qpgnfn.x b/sys/qpoe/qpgnfn.x new file mode 100644 index 00000000..c12adc76 --- /dev/null +++ b/sys/qpoe/qpgnfn.x @@ -0,0 +1,240 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +.help qp_gnfn +.nf -------------------------------------------------------------------------- +QP_GNFN -- Access the file header as a parameter list. + + list = qp_ofnl[su] (qp, template) + nchars|EOF = qp_gnfn (list, outstr, maxch) + len = qp_lenfnl (list) + qp_seekfnl (list, pos) + qp_cfnl (list) + +These routines are used to determine the names of the fields (parameters) in +the QPOE file header, e.g., to list out the header. QP_OFNLS and QP_ONFLU open +the file header (sorted or unsorted). QP_GNFN returns the next parameter name, +returning as the function value the string length of the parameter name or EOF +when the end of the list is reached. QP_SFNL seeks on or rewinds the list. +QP_CFNL closes the list descriptor. +.endhelp -------------------------------------------------------------------- + +# Size limiting definitions. +define DEF_LENOFFV 128 # initial length of keywd-offset vector +define INC_LENOFFV 128 # increment to above +define DEF_SZSBUF 1024 # initial size of string buffer +define INC_SZSBUF 1024 # increment to above + +# List descriptor. +define LEN_FL 3 +define FL_LEN Memi[$1] # number of names in list +define FL_POS Memi[$1+1] # current position +define FL_SBUF Memi[$1+2] # pointer to string buffer +define FL_OFFV Memi[$1+3] # pointer to offset vector + + +# QP_OFNLS -- Open a sorted field name list. + +pointer procedure qp_ofnls (qp, template) + +pointer qp #I QPOE descriptor +char template[ARB] #I field name template + +pointer qp_ofnl() + +begin + return (qp_ofnl (qp, template, true)) +end + + +# QP_OFNLU -- Open an unsorted field name list. + +pointer procedure qp_ofnlu (qp, template) + +pointer qp #I QPOE descriptor +char template[ARB] #I field name template + +pointer qp_ofnl() + +begin + return (qp_ofnl (qp, template, false)) +end + + +# QP_OFNL -- Open a sorted or unsorted field name list. + +pointer procedure qp_ofnl (qp, template, sort) + +pointer qp #I QPOE descriptor +char template[ARB] #I field name template +bool sort #I sort list of matched names? + +pointer sp, patbuf, pattern, sym, fl, st, offv, sbuf, ip, op +int len_offv, sz_sbuf, nsyms, nc, junk, nchars, i, nmatch + +pointer sthead(), stnext(), stname() +int patmake(), patmatch(), strlen() +define swap {junk=$1;$1=$2;$2=junk} +errchk calloc, malloc, realloc + +begin + call smark (sp) + call salloc (pattern, SZ_LINE, TY_CHAR) + call salloc (patbuf, SZ_LINE, TY_CHAR) + + # Allocate the list descriptor. + call calloc (fl, LEN_FL, TY_STRUCT) + call malloc (offv, DEF_LENOFFV, TY_INT) + call malloc (sbuf, DEF_SZSBUF, TY_CHAR) + + len_offv = DEF_LENOFFV + sz_sbuf = DEF_SZSBUF + st = QP_ST(qp) + nsyms = 0 + nc = 0 + + # Default to match all; map '*' into '?*', which is probably what + # the user intends. Match only at the beginning of line as we want + # to match only entire field name strings. + + if (template[1] == EOS) + call strcpy ("?*", Memc[pattern], SZ_LINE) + else { + op = pattern + Memc[op] = '^' + op = op + 1 + for (ip=1; template[ip] != EOS && ip < SZ_LINE; ip=ip+1) { + if (template[ip] == '*') + if (ip == 1 || (ip > 1 && template[ip-1] != ']')) { + Memc[op] = '?' + op = op + 1 + } + Memc[op] = template[ip] + op = op + 1 + } + Memc[op] = EOS + } + + # Compile the pattern matching template. + junk = patmake (Memc[pattern], Memc[patbuf], SZ_LINE) + + # Scan the symbol table and generate the unsorted list. + for (sym=sthead(st); sym != NULL; sym=stnext(st,sym)) { + if (and (S_FLAGS(sym), SF_DELETED) != 0) + next + + # Get the symbol name. + ip = stname (st, sym) + nchars = strlen (Memc[ip]) + + # Save in list if it matches. + nmatch = patmatch (Memc[ip], Memc[patbuf]) - 1 + if (nmatch > 0 && nmatch == nchars) { + nsyms = nsyms + 1 + + # Make room in offset vector? + if (nsyms > len_offv) { + len_offv = len_offv + INC_LENOFFV + call realloc (offv, len_offv, TY_INT) + } + + # Make room in string buffer? + if (nc + nchars + 1 > sz_sbuf) { + sz_sbuf = sz_sbuf + INC_SZSBUF + call realloc (sbuf, sz_sbuf, TY_CHAR) + } + + # Add the symbol. + Memi[offv+nsyms-1] = nc + 1 + call strcpy (Memc[ip], Memc[sbuf+nc], nchars) + nc = nc + nchars + 1 + } + } + + # Sort the list if indicated, else reverse the order of the list + # to get a time-ordered (FIFO) list. + + if (sort) + call strsrt (Memi[offv], Memc[sbuf], nsyms) + else { + do i = 1, nsyms / 2 + swap (Memi[offv+i-1], Memi[offv+nsyms-i]) + } + + # Finish setting up the descriptor. + FL_LEN(fl) = nsyms + FL_SBUF(fl) = sbuf + FL_OFFV(fl) = offv + + call sfree (sp) + return (fl) +end + + +# QP_GNFN -- Return the next element from the field name list. The string +# length is returned as the function value, or EOF at the end of the list. + +int procedure qp_gnfn (fl, outstr, maxch) + +pointer fl #I list descriptor +char outstr[maxch] #O output string +int maxch #I max chars out + +int pos, off, nchars +int gstrcpy() + +begin + pos = FL_POS(fl) + if (pos >= FL_LEN(fl)) + return (EOF) + + off = Memi[FL_OFFV(fl) + pos] + nchars = gstrcpy (Memc[FL_SBUF(fl)+off-1], outstr, maxch) + + FL_POS(fl) = pos + 1 + return (nchars) +end + + +# QP_LENFNL -- Return the length of (number of names in) the field name list. + +int procedure qp_lenfnl (fl) + +pointer fl #I list descriptor + +begin + return (FL_LEN(fl)) +end + + +# QP_SEEKFNL -- Seek on the field name list. + +procedure qp_seekfnl (fl, pos) + +pointer fl #I list descriptor +int pos #I desired list element, BOF, EOF + +begin + switch (pos) { + case BOF: + FL_POS(fl) = 0 + case EOF: + FL_POS(fl) = FL_LEN(fl) + default: + FL_POS(fl) = max(0, min(FL_LEN(fl), pos - 1)) + } +end + + +# QP_CFNL -- Close a field name list. + +procedure qp_cfnl (fl) + +pointer fl #I list descriptor + +begin + call mfree (FL_SBUF(fl), TY_CHAR) + call mfree (FL_OFFV(fl), TY_INT) + call mfree (fl, TY_STRUCT) +end diff --git a/sys/qpoe/qpgpar.x b/sys/qpoe/qpgpar.x new file mode 100644 index 00000000..f3307043 --- /dev/null +++ b/sys/qpoe/qpgpar.x @@ -0,0 +1,101 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include "qpoe.h" + +# QP_GETPARAM -- Lookup the named parameter in the symbol table and return +# a pointer to the scalar parameter value. A NULL pointer is returned if +# the parameter exists but does not currently have a value. The parameter +# datatype code is returned as the function value. The pointed to parameter +# value will be clobbered in the next call, hence should be used promptly. +# The data element pointed to may be a structure as well as a primitive type. + +int procedure qp_getparam (qp, param, o_pp) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +pointer o_pp #O pointer to parameter value + +int loc_pval, loc_Mem, ip, ch, elem, sz_elem, fd +pointer sp, key, fm, pp, op, sym +double pval[LEN_PVAL+1] +data pp /NULL/ + +pointer qp_gpsym() +int qp_sizeof(), fm_getfd(), qp_ctoi(), read() +errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek, read + +begin + call smark (sp) + call salloc (key, SZ_FNAME, TY_CHAR) + + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + fm = QP_FM(qp) + + # Compute pointer (Memc index) to the static pval buffer. + # Make sure that the computed pointer is double aligned. + + if (pp == NULL) { + call zlocva (pval, loc_pval) + call zlocva (Memc, loc_Mem) + pp = (loc_pval+SZ_DOUBLE - loc_Mem) / SZ_DOUBLE * SZ_DOUBLE + 1 + } + + # Extract the primary parameter name, minus any whitespace and + # subscript (e.g., "param[elem]"). + + op = key + do ip = 1, SZ_FNAME { + ch = param[ip] + if (IS_WHITE(ch)) + next + else if (ch == '[' || ch == EOS) + break + Memc[op] = ch + op = op + 1 + } + Memc[op] = EOS + + # Determine the array element (default [1]). + elem = 1 + if (param[ip] == '[') { + ip = ip + 1 + if (qp_ctoi (param, ip, elem) <= 0) + elem = 1 + } + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, Memc[key]) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + + # Check to make sure the parameter value exists, and fetch the + # value from the lfile where the parameter data is stored, setting + # the parameter value pointer to point to the stored value. + + if (elem < 1 || elem > S_NELEM(sym)) + o_pp = NULL + else { + sz_elem = qp_sizeof (qp, S_DTYPE(sym), sym, INSTANCEOF) + if (sz_elem > LEN_PVAL * SZ_DOUBLE) + call syserrs (SYS_QPPVALOVF, QP_DFNAME(qp)) + + fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0) + + call seek (fd, S_OFFSET(sym) + (elem - 1) * sz_elem) + if (read (fd, Memc[pp], sz_elem) < sz_elem) + o_pp = NULL + else if (S_DTYPE(sym) == TY_USER) + o_pp = (pp - 1) / SZ_STRUCT + 1 + else + o_pp = (pp - 1) / sz_elem + 1 + + call fm_retfd (fm, S_LFILE(sym)) + } + + call sfree (sp) + return (S_DTYPE(sym)) +end diff --git a/sys/qpoe/qpgpsym.x b/sys/qpoe/qpgpsym.x new file mode 100644 index 00000000..b18199e2 --- /dev/null +++ b/sys/qpoe/qpgpsym.x @@ -0,0 +1,90 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_GPSYM -- Lookup the named parameter in the symbol table and return +# a pointer to the symstruct describing the parameter as the function value. +# NULL is returned if the parameter is not defined, or if the named symbol is +# not a parameter. Global parameter aliases are recursively expanded. +# Local macros are not expanded at this level, since local macros are stored +# as parameters themselves. + +pointer procedure qp_gpsym (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +int n +pointer sp, pname, sym, st, sm +pointer stfind(), strefsbuf(), qm_symtab() +errchk syserrs + +#pointer ip +#int fd, nchars +#int fm_getfd(), read() +#errchk fm_getfd, read, seek + +begin + call smark (sp) + call salloc (pname, SZ_FNAME, TY_CHAR) + + st = QP_ST(qp) + sm = qm_symtab (QP_QM(qp)) + call strcpy (param, Memc[pname], SZ_FNAME) + + # First expand any aliases in the global macro symbol table. + sym = stfind (sm, param) + for (n=1; sym != NULL; n=n+1) { + if (and (S_FLAGS(sym), SF_DELETED) != 0) + break + call strcpy (strefsbuf(sm,S_OFFSET(sym)), Memc[pname], SZ_FNAME) + sym = stfind (sm, Memc[pname]) + if (n > MAX_INDIR) + call syserrs (SYS_QPMRECUR, param) + } + + # Lookup the symbol in the datafile local symbol table. Datafile + # local macros cannot be expanded in parameter references, since + # the macros are themselves stored as parameters (if macro parameters + # were expanded in parameter references, there would be no simple + # way to access the macro parameters themselves). + + sym = stfind (st, Memc[pname]) + +# Disable expansion of datafile-local macro defines. +# if (sym != NULL) { +# for (n=0; S_DTYPE(sym) == TY_MACRO; n=n+1) { +# if (and (S_FLAGS(sym), SF_DELETED) != 0) { +# break +# +# } else if (S_LFILE(sym) > 0) { +# # Macro value stored as data. +# fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_ONLY, 0) +# +# call seek (fd, S_OFFSET(sym)) +# nchars = max (0, read (fd, Memc[pname], S_NELEM(sym))) +# Memc[pname+nchars] = EOS +# ip = pname +# +# call fm_retfd (QP_FM(qp), S_LFILE(sym)) +# +# } else { +# # Macro value stored in symbol table. +# ip = strefsbuf (st, S_OFFSET(sym)) +# } +# +# # Macro recursion. +# if (n > MAX_INDIR) +# call syserrs (SYS_QPMRECUR, param) +# } +# } + + # Don't "find" the symbol if it has been deleted. + if (sym != NULL) + if (and (S_FLAGS(sym), SF_DELETED) != 0) + sym = NULL + + call sfree (sp) + return (sym) +end diff --git a/sys/qpoe/qpgstr.x b/sys/qpoe/qpgstr.x new file mode 100644 index 00000000..91a03a0f --- /dev/null +++ b/sys/qpoe/qpgstr.x @@ -0,0 +1,42 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_GSTR -- Return the string value of the named parameter. + +int procedure qp_gstr (qp, param, outstr, maxch) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char outstr[maxch] #O receives string value +int maxch #I max chars out + +pointer sym, fm +int nchars, fd +pointer qp_gpsym() +int fm_getfd(), read() +errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + fm = QP_FM(qp) + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + else if (!(S_DTYPE(sym) == TY_CHAR || S_DTYPE(sym) == TY_USER)) + call syserrs (SYS_QPBADCONV, param) + + # Fetch the string value from the lfile where the data is stored. + fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0) + call seek (fd, S_OFFSET(sym)) + + nchars = max (0, read (fd, outstr, min(S_NELEM(sym),maxch))) + outstr[nchars+1] = EOS + + call fm_retfd (fm, S_LFILE(sym)) + return (nchars) +end diff --git a/sys/qpoe/qpinherit.x b/sys/qpoe/qpinherit.x new file mode 100644 index 00000000..495966f4 --- /dev/null +++ b/sys/qpoe/qpinherit.x @@ -0,0 +1,57 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <error.h> +include "qpoe.h" + +# QP_INHERIT -- Copy all the inheritable parameters from one datafile to +# another. + +procedure qp_inherit (n_qp, o_qp, out) + +pointer n_qp #I QPOE descriptor of new datafile +pointer o_qp #I QPOE descriptor of old datafile +int out #I output stream for verbose messages, or NULL + +int nsyms, i +pointer sp, n_st, o_st, sym, op, pname, syms +pointer sthead(), stnext(), stname() +int qp_accessf() + +begin + call smark (sp) + + n_st = QP_ST(n_qp) + o_st = QP_ST(o_qp) + + # Count the symbols to be copied. + nsyms = 0 + for (sym=sthead(o_st); sym != NULL; sym=stnext(o_st,sym)) + if (and (S_FLAGS(sym), SF_DELETED) == 0) + if (and (S_FLAGS(sym), SF_INHERIT) != 0) + nsyms = nsyms + 1 + + # Construct a reversed array of symbol pointers. + call salloc (syms, nsyms, TY_POINTER) + op = syms + nsyms - 1 + for (sym=sthead(o_st); sym != NULL; sym=stnext(o_st,sym)) + if (and (S_FLAGS(sym), SF_DELETED) == 0) + if (and (S_FLAGS(sym), SF_INHERIT) != 0) { + Memi[op] = sym + op = op - 1 + } + + # Copy each symbol. + do i = 1, nsyms { + pname = stname (o_st, Memi[syms+i-1]) + if (qp_accessf (n_qp, Memc[pname]) == YES) { + if (out != NULL) { + call fprintf (out, + "parameter `%s' already exists, not copied\n") + call pargstr (Memc[pname]) + } + } else iferr (call qp_copyf (o_qp, Memc[pname], n_qp, Memc[pname])) + call erract (EA_WARN) + } + + call sfree (sp) +end diff --git a/sys/qpoe/qpio.h b/sys/qpoe/qpio.h new file mode 100644 index 00000000..91b58c73 --- /dev/null +++ b/sys/qpoe/qpio.h @@ -0,0 +1,140 @@ +# QPIO.H -- Definitions for the QPOE event i/o subpackage. + +# Default parameter and domain names. +define DEF_BLOCK "defblock" # header param - default block factor +define DEF_XBLOCK "defxblock" # header param - default X block factor +define DEF_YBLOCK "defyblock" # header param - default Y block factor +define DEF_MASK "defmask" # header param - default region mask +define DEF_FILTER "deffilt" # header param - default event filter +define DEF_EVENTTYPE "event" # default name of user event datatype +define DEF_EVENTPARAM "events" # default event-list parameter + +# QPIO keywords recognized in expressions (abbreviations permitted). +define KEYWORDS "|block|xblock|yblock|debug|filter|key|noindex|param|mask|rect|" + +define KW_BLOCK 1 # blocking factor for image matrix +define KW_XBLOCK 2 # X blocking factor for image matrix +define KW_YBLOCK 3 # Y blocking factor for image matrix +define KW_DEBUG 4 # debug level (integer, 0=nodebug) +define KW_FILTER 5 # event attribute filter +define KW_KEY 6 # event key (Y,X) fields (e.g.(s10,s8)) +define KW_NOINDEX 7 # don't use index even if present +define KW_PARAM 8 # name of event list header parameter +define KW_MASK 9 # region mask +define KW_RECT 10 # rectangle (bounding box) for i/o + +# Size limiting definitions. +define SZ_EVLIST 1024 # event list buffer size (arbitrary) +define NDIM 2 # all QPOE images are 2-dim + +# The main QPIO descriptor. +define LEN_IODES 82 + +# general +define IO_QP Memi[$1] # backpointer to QPOE descriptor +define IO_MODE Memi[$1+1] # read_only or new_file +define IO_DEBUG Memi[$1+2] # debug level +define IO_NLINES Memi[$1+3] # number of image lines (physical) +define IO_NCOLS Memi[$1+4] # number of image columns (physical) +define IO_XBLOCK Memr[P2R($1+5)] # blocking factor for qpio_readpix +define IO_YBLOCK Memr[P2R($1+6)] # blocking factor for qpio_readpix +define IO_OPTBUFSIZE Memi[$1+7] # optbufsize for FIO (qpio_readpix) +define IO_NOINDEX Memi[$1+8] # don't use indexed extraction +define IO_NODEFFILT Memi[$1+9] # disable use of default filter +define IO_NODEFMASK Memi[$1+10] # disable use of default mask +define IO_PARAM Memi[$1+11] # pointer to buffer with param name +define IO_PSYM Memi[$1+12] # symbol table entry for parameter +define IO_MASK Memi[$1+13] # pointer to buffer with mask name +define IO_MDEPTH Memi[$1+14] # mask depth, bits +define IO_EXCLOSE Memi[$1+15] # qpex was opened by qpio +define IO_PLCLOSE Memi[$1+16] # mask was opened by qpio +define IO_PL Memi[$1+17] # PLIO (mask) pointer +define IO_EX Memi[$1+18] # QPEX (event attribute filter) pointer +define IO_FD Memi[$1+19] # file descriptor of open lfile +define IO_LF Memi[$1+20] # lfile where event list is stored +define IO_CHAN Memi[$1+21] # i/o channel of open lfile +# events +define IO_DD Memi[$1+22] # pointer to domain descriptor +define IO_EVXOFF Memi[$1+23] # offset of X field used for extraction +define IO_EVXTYPE Memi[$1+24] # datatype of X field +define IO_EVYOFF Memi[$1+25] # offset of Y field used for extraction +define IO_EVYTYPE Memi[$1+26] # datatype of Y field +define IO_EVENTLEN Memi[$1+27] # length of event struct, shorts +define IO_MINEVL Memi[$1+28] # pointer to min event for full list +define IO_MAXEVL Memi[$1+29] # pointer to max event for full list +# buckets +define IO_SZBBUCKET Memi[$1+30] # event file bucket size, bytes +define IO_BUCKETLEN Memi[$1+31] # nevents per bucket (excl. min/max) +define IO_NEVENTS Memi[$1+32] # total data events in event list +define IO_FBOFF Memi[$1+33] # lfile offset of first bucket +define IO_EVMINOFF Memi[$1+34] # offset to the MIN event in a bucket +define IO_EVMAXOFF Memi[$1+35] # offset to the MAX event in a bucket +# index +define IO_INDEXLEN Memi[$1+38] # length of index (same as nlines) +define IO_IXXOFF Memi[$1+39] # offset of X field used in index +define IO_IXXTYPE Memi[$1+40] # datatype of X field used in index +define IO_IXYOFF Memi[$1+41] # offset of Y field used in index +define IO_IXYTYPE Memi[$1+42] # datatype of Y field used in index +define IO_YOFFVP Memi[$1+43] # pointer to Y-index array (len=nlines) +define IO_YLENVP Memi[$1+44] # pointer to Y-line length array +define IO_YOFFVOFF Memi[$1+45] # lfile offset of stored YOFFV +define IO_YOFFVLEN Memi[$1+46] # length, words, of compressed YOFFV +define IO_YLENVOFF Memi[$1+47] # lfile offset of stored YLENV +define IO_YLENVLEN Memi[$1+48] # length, words, of compressed YLENV +# i/o +define IO_ACTIVE Memi[$1+50] # set once i/o begins +define IO_IOTYPE Memi[$1+51] # type of i/o selected for BB +define IO_LINEIO Memi[$1+52] # flag - BB width is full line width +define IO_RMUSED Memi[$1+53] # flag - region mask used in this BB +define IO_BBUSED Memi[$1+54] # flag - bounding box in use +define IO_BBMASK Memi[$1+55] # BB region mask subras, nonindexed i/o +define IO_RL Memi[$1+56] # range list pointer +define IO_RLI Memi[$1+57] # range list index +define IO_EVI Memi[$1+58] # event index into event list (for i/o) +define IO_EV1 Memi[$1+59] # event index of first event on line +define IO_EV2 Memi[$1+60] # event index of last event on line +define IO_BP Memi[$1+61] # pointer to bucket buffer +define IO_BKNO Memi[$1+62] # bucket number +define IO_BKFIRSTEV Memi[$1+63] # event index of first event in bucket +define IO_BKLASTEV Memi[$1+64] # event index of last event in bucket +# (avail) +define IO_V Meml[$1+70+$2-1]# current vector +define IO_VS Meml[$1+72+$2-1]# start vector +define IO_VE Meml[$1+74+$2-1]# end vector +define IO_VN Meml[$1+76+$2-1]# size of section +define IO_VSDEF Meml[$1+78+$2-1]# default start vector +define IO_VEDEF Meml[$1+80+$2-1]# default end vector + +# Handy macros. +define IO_MINEVB (IO_BP($1)+IO_EVMINOFF($1)) +define IO_MAXEVB (IO_BP($1)+IO_EVMAXOFF($1)) +define EVI_TO_BUCKET ((($2)-1)/IO_BUCKETLEN($1)+1) +define BUCKET_TO_EVI ((($2)-1)*IO_BUCKETLEN($1)+1) + +# I/O types (specially optimized code for each case). +define NoINDEX_NoRMorBB 0 # nonindexed, no RM no BB +define NoINDEX_RMorBB 1 # nonindexed, maybe RM or BB +define INDEX_NoRMorBB 2 # indexed, no RM or BB +define INDEX_RMorBB 3 # indexed, maybe RM or BB +define NoDATA_NoAREA 4 # no events can be returned + +# Stored Event List header (one per stored event list). +define LEN_EHDES 18 +define EH_NEVENTS Memi[$1] # total data events in event list +define EH_EVENTLEN Memi[$1+1] # event length, shorts +define EH_SZBBUCKET Memi[$1+2] # event file bucket size, bytes +define EH_BUCKETLEN Memi[$1+3] # nevents per bucket (excl. min/max) +define EH_FBOFF Memi[$1+4] # lfile offset of first bucket +define EH_EVMINOFF Memi[$1+5] # offset to the MIN event in a bucket +define EH_EVMAXOFF Memi[$1+6] # offset to the MAX event in a bucket +define EH_MINEVLOFF Memi[$1+7] # offset of stored MINEVL +define EH_MAXEVLOFF Memi[$1+8] # offset of stored MAXEVL +define EH_INDEXLEN Memi[$1+9] # length of index (same as nlines) +define EH_YOFFVOFF Memi[$1+10] # lfile offset of stored YOFFV +define EH_YOFFVLEN Memi[$1+11] # length, words, of compressed YOFFV +define EH_YLENVOFF Memi[$1+12] # lfile offset of stored YLENV +define EH_YLENVLEN Memi[$1+13] # length, words, of compressed YLENV +define EH_IXXOFF Memi[$1+14] # event offset of indexed X field +define EH_IXYOFF Memi[$1+15] # event offset of indexed Y field +define EH_IXXTYPE Memi[$1+16] # datatype of indexed X field +define EH_IXYTYPE Memi[$1+17] # datatype of indexed Y field diff --git a/sys/qpoe/qpioclose.x b/sys/qpoe/qpioclose.x new file mode 100644 index 00000000..78893487 --- /dev/null +++ b/sys/qpoe/qpioclose.x @@ -0,0 +1,49 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpio.h" + +# QPIO_CLOSE -- Close the QPIO descriptor. If writing to the event list, +# the output bucket is automatically flushed and the event list header updated. + +procedure qpio_close (io) + +pointer io #I QPIO descriptor + +begin + if (IO_DEBUG(io) > 1) { + call eprintf ("qpio_close (%xX)\n") + call pargi (io) + } + + call qpio_sync (io) + + if (IO_EX(io) != NULL && IO_EXCLOSE(io) == YES) + call qpex_close (IO_EX(io)) + if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES) + call pl_close (IO_PL(io)) + if (IO_FD(io) != NULL) + call close (IO_FD(io)) + + if (IO_BP(io) != NULL) + call mfree (IO_BP(io), TY_SHORT) + if (IO_RL(io) != NULL) + call mfree (IO_RL(io), TY_INT) + if (IO_MINEVL(io) != NULL) + call mfree (IO_MINEVL(io), TY_SHORT) + if (IO_MAXEVL(io) != NULL) + call mfree (IO_MAXEVL(io), TY_SHORT) + if (IO_YLENVP(io) != NULL) + call mfree (IO_YLENVP(io), TY_INT) + if (IO_YOFFVP(io) != NULL) + call mfree (IO_YOFFVP(io), TY_INT) + if (IO_DD(io) != NULL) + call mfree (IO_DD(io), TY_STRUCT) + if (IO_BBMASK(io) != NULL) + call plr_close (IO_BBMASK(io)) + if (IO_PARAM(io) != NULL) + call mfree (IO_PARAM(io), TY_CHAR) + if (IO_MASK(io) != NULL) + call mfree (IO_MASK(io), TY_CHAR) + + call mfree (io, TY_STRUCT) +end diff --git a/sys/qpoe/qpiogetev.gx b/sys/qpoe/qpiogetev.gx new file mode 100644 index 00000000..e6fbc612 --- /dev/null +++ b/sys/qpoe/qpiogetev.gx @@ -0,0 +1,467 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <pmset.h> +include "../qpio.h" + +define RLI_NEXTLINE 9998 +define RLI_INITIALIZE 9999 +define SZ_CODE 7 + +# QPIO_GETEVENTS -- Return a sequence of events sharing the same mask value +# which satisfy the current event attribute filter. The returned events will +# be only those in a rectangular subregion of the image (specified by a prior +# call to qpio_setrange) which are also visible through the current mask. +# Sequences of events are returned in storage order until the region is +# exhausted, at which time EOF is returned. +# +# NOTE - If debug statements (printfs) are placed in this code they will cause +# i/o problems at runtime due to reentrancy, since this routine is called in +# a low level FIO pseudodevice driver (QPF). This is also true of any of the +# routines called by this procedure, and of the related routine QPIO_READPIX. + +int procedure qpio_gvtevents (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int status +char code[SZ_CODE] +int qpx_gvs(), qpx_gvi(), qpx_gvl(), qpx_gvr(), qpx_gvd() +errchk syserrs +define err_ 91 + +begin + # The generic routines currently require that X,Y be the same type. + # It wouldn't be hard to remove this restriction if necessary, but + # it simplifies things and I doubt if a mixed types feature would + # be used very often. + + if (IO_EVXTYPE(io) != IO_EVYTYPE(io)) + goto err_ + + # Get the events. + switch (IO_EVXTYPE(io)) { + case TY_SHORT: + status = qpx_gvs (io, o_ev, maskval, maxev, o_nev) + case TY_INT: + status = qpx_gvi (io, o_ev, maskval, maxev, o_nev) + case TY_LONG: + status = qpx_gvl (io, o_ev, maskval, maxev, o_nev) + case TY_REAL: + status = qpx_gvr (io, o_ev, maskval, maxev, o_nev) + case TY_DOUBLE: + status = qpx_gvd (io, o_ev, maskval, maxev, o_nev) + default: +err_ call sprintf (code, SZ_CODE, "%d") + call pargi (IO_EVXTYPE(io)) + call syserrs (SYS_QPINVEVT, code) + } + + return (status) +end + + +$for (silrd) + +# QPX_GV -- Internal generic code for qpio_getevents. There is one copy +# of this routine for each event coordinate datatype. The optimization +# strategy used here assumes that executing qpio_gv is much more expensive +# than building the call in qpio_getevents. This will normally be the case +# for a large event list or a complex expression, otherwise the operation +# is likely to be fast enough that it doesn't matter anyway. + +int procedure qpx_gv$t (io, o_ev, maskval, maxev, o_nev) + +pointer io #I QPIO descriptor +pointer o_ev[maxev] #O receives the event struct pointers +int maskval #O receives the mask value of the events +int maxev #I max events out +int o_nev #O same as function value (nev_out|EOF) + +int x1, x2, y1, y2, xs, xe, ys, ye, x, y +pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp +bool useindex, lineio, bbused, rmused, nodata +int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj +int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i +int ev_xoff, ev_yoff + +pointer plr_open() +bool pl_linenotempty(), pl_sectnotempty() +int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix() + +define swap {temp=$1;$1=$2;$2=temp} +define putevent_ 91 +define again_ 92 +define done_ 93 +define exit_ 94 + +begin + pl = IO_PL(io) # pixel list (region mask) descriptor + rl = IO_RL(io) # range list buffer + bp = IO_BP(io) # bucket buffer (type short) + ex = IO_EX(io) # QPEX (EAF) descriptor + + # The following is executed when the first i/o is performed on a new + # region, to select the most efficient type of i/o to be performed, + # and initialize the i/o parameters for that case. The type of i/o + # to be performed depends upon whether or not an index can be used, + # and whether or not there is a region mask (RM) or bounding box (BB). + # The presence or absence of an event attribute filter (EAF) is not + # separated out as a special case, as it is quick and easy to test + # for the presence of an EAF and apply one it if it exists. + + if (IO_ACTIVE(io) == NO) { + # Check for an index. We have an index if the event list is + # indexed, and the index is defined on the Y-coordinate we will + # be using for extraction. + + useindex = (IO_INDEXLEN(io) == IO_NLINES(io) && + IO_EVYOFF(io) == IO_IXYOFF(io) && + IO_NOINDEX(io) == NO) + + # Initialize the V and VN vectors. + do i = 1, NDIM { + IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1 + if (IO_VN(io,i) < 0) { + swap (IO_VS(io,i), IO_VE(io,i)) + IO_VN(io,i) = -IO_VN(io,i) + } + } + call amovi (IO_VS(io,1), IO_V(io,1), NDIM) + + # Determine if full lines are to be accessed, and if a bounding + # box (subraster of the image) is defined. + + lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io)) + bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io)) + + # Determine if region mask data is to be used and if there is any + # data to be read. + + nodata = (IO_NEVENTS(io) <= 0) + rmused = false + + if (pl != NULL) + if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM)) + rmused = true + else + nodata = true + + # Select the optimal type of i/o to be used for extraction. + if (nodata) { + IO_IOTYPE(io) = NoDATA_NoAREA + useindex = false + bbused = false + + } else if (bbused || rmused) { + if (useindex) + IO_IOTYPE(io) = INDEX_RMorBB + else + IO_IOTYPE(io) = NoINDEX_RMorBB + + } else { + # If we are reading the entire image (no bounding box) and + # we are not using a mask, then there is no point in using + # indexed i/o. + + IO_IOTYPE(io) = NoINDEX_NoRMorBB + useindex = false + } + + # Initialize the range list data if it will be used. + if (useindex) { + # Dummy range specifying full line segment. + RLI_LEN(rl) = RL_FIRST + RLI_AXLEN(rl) = IO_NCOLS(io) + + rp = rl + ((RL_FIRST - 1) * RL_LENELEM) + Memi[rp+RL_XOFF] = IO_VS(io,1) + Memi[rp+RL_NOFF] = IO_VN(io,1) + Memi[rp+RL_VOFF] = 1 + + IO_RLI(io) = RLI_INITIALIZE + } + + # Open the mask for random access if i/o is not indexed and + # a region mask is used. + + bbmask = IO_BBMASK(io) + if (bbmask != NULL) + call plr_close (bbmask) + + if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) { + bbmask = plr_open (pl, v, 0) # (v is never referenced) + call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2), + IO_VE(io,1),IO_VE(io,2)) + call plr_getlut (bbmask, + bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock) + } + + # Update the QPIO descriptor. + IO_LINEIO(io) = btoi(lineio) + IO_RMUSED(io) = btoi(rmused) + IO_BBUSED(io) = btoi(bbused) + IO_BBMASK(io) = bbmask + + IO_EVI(io) = 1 + IO_BKNO(io) = 0 + IO_BKLASTEV(io) = 0 + + IO_ACTIVE(io) = YES + } + + # Initialize event extraction parameters. + szs_event = IO_EVENTLEN(io) + maskval = 0 + nev = 0 + + ev_xoff = IO_EVXOFF(io) + ev_yoff = IO_EVYOFF(io) + + # Extract events using the most efficient type of i/o for the given + # selection critera (index, mask, BB, EAF, etc.). +again_ + switch (IO_IOTYPE(io)) { + case NoDATA_NoAREA: + # We know in advance that there are no events to be returned, + # either because there is no data, or the area of the region + # mask within the bounding box is empty. + + goto exit_ + + case NoINDEX_NoRMorBB: + # This is the simplest case; no index, region mask, or bounding + # box. Read and output all events in sequence. + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Copy out the event pointers. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1) + + do i = 1, nev { + o_ev[i] = ev + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + maskval = 1 + + case NoINDEX_RMorBB: + # Fully general selection, including any combination of bounding + # box, region mask, or EAF, but no index, either because there is + # no index for this event list, or the index is for a different Y + # attribute than the one being used for extraction. + + bbused = (IO_BBUSED(io) == YES) + x1 = IO_VS(io,1); x2 = IO_VE(io,1) + y1 = IO_VS(io,2); y2 = IO_VE(io,2) + + # Refill the event bucket? + while (IO_EVI(io) > IO_BKLASTEV(io)) { + # Get the next bucket. + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Reject buckets that do not contain any events lying + # within the specified bounding box, if any. + + if (bbused) { + ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_PIXEL + 1 + $if (datatype == rd) + xs = Mem$t[ev_p+ev_xoff] + 0.5 + ys = Mem$t[ev_p+ev_yoff] + 0.5 + $else + xs = Mem$t[ev_p+ev_xoff] + ys = Mem$t[ev_p+ev_yoff] + $endif + + ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_PIXEL + 1 + $if (datatype == rd) + xe = Mem$t[ev_p+ev_xoff] + 0.5 + ye = Mem$t[ev_p+ev_yoff] + 0.5 + $else + xe = Mem$t[ev_p+ev_xoff] + ye = Mem$t[ev_p+ev_yoff] + $endif + + if (xs > x2 || xe < x1 || ys > y2 || ye < y1) + IO_EVI(io) = IO_BKLASTEV(io) + 1 + } + } + + # Copy out any events which pass the region mask and which share + # the same mask value. Note that in this case, to speed mask + # value lookup at random mask coordinates, the region mask for + # the bounding box is stored as a populated array in the QPIO + # descriptor. + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event + bbmask = IO_BBMASK(io) + mval = 0 + + do i = IO_EVI(io), IO_BKLASTEV(io) { + # Get event x,y coordinates in whatever coord system. + ev = ev + szs_event + ev_p = (ev - 1) * SZ_SHORT / SZ_PIXEL + 1 + + $if (datatype == rd) + x = Mem$t[ev_p+ev_xoff] + 0.5 + y = Mem$t[ev_p+ev_yoff] + 0.5 + $else + x = Mem$t[ev_p+ev_xoff] + y = Mem$t[ev_p+ev_yoff] + $endif + + # Reject events lying outside the bounding box. + if (bbused) + if (x < x1 || x > x2 || y < y1 || y > y2) + next + + # Take a shortcut if no region mask is in effect for this BB. + if (bbmask == NULL) + goto putevent_ + + # Get the mask pixel associated with this event. + ii = (x - 1) / bb_xblock + jj = (y - 1) / bb_yblock + mval = Memi[bb_bufp + jj*bb_xsize + ii] + if (mval < 0) + mval = plr_getpix (bbmask, x, y) + + # Accumulate points lying in the first nonzero mask range + # encountered. + + if (mval != 0) { + if (maskval == 0) + maskval = mval + if (mval == maskval) { +putevent_ if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } else + break + } + } + + IO_EVI(io) = i + + case INDEX_NoRMorBB, INDEX_RMorBB: + # General extraction for indexed data. Process successive ranges + # and range lists until we get at least one event which lies within + # the bounding box, within a range, and which passes the event + # attribute filter, if one is in use. + + # If the current range list (mask line) has been exhausted, advance + # to the next line which contains both ranges and events. A range + # list is used to specify the bounding box even if we don't have + # a nonempty region mask within the BB. + + if (IO_RLI(io) > RLI_LEN(rl)) { + repeat { + y = IO_V(io,2) + if (IO_RLI(io) == RLI_INITIALIZE) + IO_RLI(io) = RL_FIRST + else + y = y + 1 + + if (y > IO_VE(io,2)) { + if (nev <= 0) { + o_nev = EOF + return (EOF) + } else + goto done_ + } + + IO_V(io,2) = y + evidx = Memi[IO_YOFFVP(io)+y-1] + + if (evidx > 0) { + if (IO_RMUSED(io) == YES) { + if (IO_LINEIO(io) == YES) { + if (!pl_linenotempty (pl,IO_V(io,1))) + next + } else { + v[1] = IO_VE(io,1); v[2] = y + if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM)) + next + } + call pl_glri (pl, IO_V(io,1), Memi[rl], + IO_MDEPTH(io), IO_VN(io,1), PIX_SRC) + } + IO_RLI(io) = RL_FIRST + } + } until (IO_RLI(io) <= RLI_LEN(rl)) + + IO_EVI(io) = evidx + IO_EV1(io) = evidx + IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1 + } + + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + goto exit_ + + # Compute current range parameters and initialize event pointer. + rp = rl + (IO_RLI(io) - 1) * RL_LENELEM + x1 = Memi[rp+RL_XOFF] + x2 = x1 + Memi[rp+RL_NOFF] - 1 + maskval = Memi[rp+RL_VOFF] + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + evtop = min (IO_EV2(io), IO_BKLASTEV(io)) + + # Extract events from bucket which lie within the current range + # of the current line. This is the inner loop of indexed event + # extraction, ignoring event attribute filtering. + + do i = IO_EVI(io), evtop { + ev_p = (ev - 1) * SZ_SHORT / SZ_PIXEL + 1 + $if (datatype == rd) + x = Mem$t[ev_p+ev_xoff] + 0.5 + $else + x = Mem$t[ev_p+ev_xoff] + $endif + if (x >= x1) { + if (x > x2) { + IO_RLI(io) = IO_RLI(io) + 1 + break + } else if (nev >= maxev) + break + nev = nev + 1 + o_ev[nev] = ev + } + ev = ev + szs_event + } + + IO_EVI(io) = i + if (i > IO_EV2(io)) + IO_RLI(io) = RLI_NEXTLINE + } +done_ + # Apply the event attribute filter if one is defined; repeat + # the whole process if we don't end up with any events. + + if (nev > 0) + if (ex != NULL) + nev = qpex_evaluate (ex, o_ev, o_ev, nev) + if (nev <= 0) + goto again_ +exit_ + o_nev = nev + if (o_nev <= 0) + o_nev = EOF + + return (o_nev) +end + +$endfor diff --git a/sys/qpoe/qpiogetfil.x b/sys/qpoe/qpiogetfil.x new file mode 100644 index 00000000..c653f6b5 --- /dev/null +++ b/sys/qpoe/qpiogetfil.x @@ -0,0 +1,123 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpoe.h" +include "qpio.h" + +# QPIO_GETFILTER -- Get the current filtering parameters governing event +# extraction via QPIO. These are the QPIO parameters (region mask, blocking +# factor, coordinate system, etc.) plus the event attribute filter. We leave +# it up to QPEX to reconstruct the EAF to allow for any dynamic edits which +# may have taken place, e.g., via qpio_setfilter. + +int procedure qpio_getfilter (io, outstr, maxch) + +pointer io #I QPIO descriptor +char outstr[maxch] #O where to put the filter text +int maxch #I max chars out + +pointer sp, buf, bp +int op, dtype[2], offset[2], i +int sizeof(), gstrcpy(), qpex_getfilter() +define ovfl_ 91 + +begin + call smark (sp) + call salloc (buf, SZ_TEXTBUF, TY_CHAR) + + op = 1 + + # Report on QPIO parameters first. + + # Parameter name. + call sprintf (Memc[buf], SZ_TEXTBUF, "param=%s,") + call pargstr (Memc[IO_PARAM(io)]) + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + + # Coordinate system. + dtype[1] = IO_EVXTYPE(io); dtype[2] = IO_EVYTYPE(io) + offset[1] = IO_EVXOFF(io); offset[2] = IO_EVYOFF(io) + + call sprintf (Memc[buf], SZ_TEXTBUF, "key=(%c%d,%c%d),") + do i = 1, 2 { + switch (dtype[i]) { + case TY_SHORT: + call pargi ('s') + case TY_INT: + call pargi ('i') + case TY_LONG: + call pargi ('l') + case TY_REAL: + call pargi ('r') + case TY_DOUBLE: + call pargi ('d') + default: + call pargi ('?') + } + + call pargi (offset[i] * sizeof(dtype[i]) * SZB_CHAR) + } + + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + + # Blocking factor for generating pixels. + call sprintf (Memc[buf], SZ_TEXTBUF, "block=%0.4gx%0.4g, ") + call pargr (IO_XBLOCK(io)) + call pargr (IO_YBLOCK(io)) + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + + # Region mask, if any. + if (Memc[IO_MASK(io)] != EOS) { + call sprintf (Memc[buf], SZ_TEXTBUF, "mask=%s,") + call pargstr (Memc[IO_MASK(io)]) + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + } + + # Debug level, if debug messages enabled. + if (IO_DEBUG(io) > 0) { + call sprintf (Memc[buf], SZ_TEXTBUF, "debug=%d,") + call pargi (IO_DEBUG(io)) + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + } + + # Noindex flag, if enabled. + if (IO_NOINDEX(io) > 0) { + call sprintf (Memc[buf], SZ_TEXTBUF, "noindex=%b,") + call pargi (IO_NOINDEX(io)) + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + } + + # Event attribute filter. + if (IO_EX(io) != NULL) { + bp = buf + gstrcpy ("filter=(", Memc[buf], SZ_TEXTBUF) + bp = bp + qpex_getfilter (IO_EX(io), Memc[bp], SZ_TEXTBUF-8) + Memc[bp] = ')'; bp = bp + 1 + Memc[bp] = EOS + op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1) + if (op > maxch) + goto ovfl_ + } else if (op > 1) { + # Clobber trailing comma. + op = op - 1 + outstr[op] = EOS + } + + call sfree (sp) + return (op - 1) +ovfl_ + call sfree (sp) + outstr[maxch+1] = EOS + return (maxch) +end diff --git a/sys/qpoe/qpiogetrg.x b/sys/qpoe/qpiogetrg.x new file mode 100644 index 00000000..bc2b0272 --- /dev/null +++ b/sys/qpoe/qpiogetrg.x @@ -0,0 +1,19 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpio.h" + +# QPIO_GETRANGE -- Get the current range in X and Y within which events will +# be extracted by qpio_getevents. + +int procedure qpio_getrange (io, vs, ve, maxdim) + +pointer io #I QPIO descriptor +int vs[ARB] #O start vector (lower left corner) +int ve[ARB] #O end vector (upper right corner) +int maxdim #I vector length (ndim=2 at present) + +begin + call amovi (IO_VS(io,1), vs, maxdim) + call amovi (IO_VE(io,1), ve, maxdim) + return (NDIM) +end diff --git a/sys/qpoe/qpiolmask.x b/sys/qpoe/qpiolmask.x new file mode 100644 index 00000000..979de478 --- /dev/null +++ b/sys/qpoe/qpiolmask.x @@ -0,0 +1,119 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <plset.h> +include "qpoe.h" +include "qpio.h" + +# QPIO_LOADMASK -- Load the named region mask into the QPIO descriptor. +# The mask name may be the name of a header parameter containing the mask +# as the stored array value (TY_OPAQUE parameter), the name of a header +# parameter containing the name of the mask (TY_CHAR), or the name of a +# mask storage file (.pl extension). + +procedure qpio_loadmask (io, mask, merge) + +pointer io #I QPIO descriptor +char mask[ARB] #I mask to be loaded +int merge #I merge with old mask? + +int niter +int naxes, axlen[PL_MAXDIM], v[PL_MAXDIM] +pointer sp, title, mp, sym, plbuf, qp, o_pl, n_pl, b_pl + +pointer pl_open(), qp_gpsym() +int strmatch(), qp_accessf(), qp_read(), qp_gstr() +errchk pl_open, pl_close, pl_loadf, qp_read, syserrs, qp_gstr, malloc +define tryfile_ 91 + +begin + call smark (sp) + call salloc (title, SZ_FNAME, TY_CHAR) + call salloc (mp, SZ_FNAME, TY_CHAR) + + if (IO_DEBUG(io) > 0) { + call eprintf ("load mask `%s'\n") + call pargstr (mask) + } + + qp = IO_QP(io) + o_pl = IO_PL(io) + call strcpy (mask, Memc[mp], SZ_FNAME) + + # Open new mask. + for (niter=0; Memc[mp] != EOS; niter=niter+1) { + if (strmatch (Memc[mp], ".pl$") > 0) { + # Mask is stored in a file. +tryfile_ + n_pl = pl_open (NULL) + call pl_loadf (n_pl, Memc[mp], Memc[title], SZ_FNAME) + + } else if (qp_accessf (qp, Memc[mp]) == YES) { + # Named parameter contains or points to mask. + + sym = qp_gpsym (qp, Memc[mp]) + if (S_DTYPE(sym) == TY_OPAQUE) { + # Parameter value is stored mask. + call salloc (plbuf, S_NELEM(sym) / SZ_SHORT, TY_SHORT) + if (qp_read (qp, Memc[mp], Mems[plbuf], S_NELEM(sym), 1, + "opaque") < S_NELEM(sym)) { + call syserrs (SYS_QPBADVAL, Memc[mp]) + } else { + n_pl = pl_open (plbuf) # no deref + } + + } else if (S_DTYPE(sym) == TY_CHAR) { + # Parameter value is pointer to mask. + if (qp_gstr (qp, Memc[mp], Memc[mp], SZ_FNAME) > 0) { + if (niter < MAX_INDIR) + next + else + call syserrs (SYS_QPMRECUR, Memc[mp]) + } + } else + goto tryfile_ + } else + goto tryfile_ + + break + } + + # Check that mask and image are the same size, and get mask depth. + call pl_gsize (n_pl, naxes, axlen, IO_MDEPTH(io)) + if (axlen[1] != IO_NCOLS(io) || axlen[2] != IO_NLINES(io)) + call syserrs (SYS_QPPLSIZE, Memc[mp]) + + # Merge the old and new mask if so indicated. The result mask is the + # same as the new mask, but only those pixels also present (nonzero) + # in the old mask are preserved. + + if (merge == YES && o_pl != NULL) { + b_pl = pl_open (NULL) + call amovkl (1, v, PL_MAXDIM) + call pl_ssize (b_pl, naxes, axlen, 1) + call pl_rop (o_pl, v, b_pl, v, axlen, PIX_SRC) + call pl_rop (b_pl, v, n_pl, v, axlen, and(PIX_SRC,PIX_DST)) + call pl_close (b_pl) + } + + # Close old mask, if any. + if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES) + call pl_close (IO_PL(io)) + + # Install new mask. + IO_PL(io) = n_pl + IO_PLCLOSE(io) = YES + call strcpy (Memc[mp], Memc[IO_MASK(io)], SZ_FNAME) + + # Allocate a range list buffer if i/o is indexed. + if (IO_INDEXLEN(io) > 0) { + if (IO_RL(io) != NULL) + call mfree (IO_RL(io), TY_INT) + if (IO_PL(io) != NULL) + call malloc (IO_RL(io), RL_MAXLEN(IO_PL(io)), TY_INT) + else + call malloc (IO_RL(io), RL_LENELEM*2, TY_INT) + } + + call sfree (sp) +end diff --git a/sys/qpoe/qpiolwcs.x b/sys/qpoe/qpiolwcs.x new file mode 100644 index 00000000..76c1650f --- /dev/null +++ b/sys/qpoe/qpiolwcs.x @@ -0,0 +1,50 @@ +include "qpio.h" + +# QPIO_LOADWCS -- Load the WCS, if any, from the QPOE file associated with the +# given QPIO descriptor, into an open MWCS descriptor. This is equivalent to +# QP_LOADWCS except that the Lterm is updated to reflect the current blocking +# factor and rect (if any) used for rasterization. In the resultant WCS, the +# logical coordinate system gives the pixel coordinates of the sampled rect. + +pointer procedure qpio_loadwcs (io) + +pointer io #I QPIO descriptor + +pointer qp, mw +int ndim, i, j +double ltv_1[NDIM], ltv_2[NDIM], ltm[NDIM,NDIM] +pointer qp_loadwcs() +errchk qp_loadwcs + +begin + qp = IO_QP(io) + mw = qp_loadwcs (qp) + ndim = NDIM + + # Formalize the transformation. + ltv_1[1] = IO_VSDEF(io,1) - 1 + ltv_1[2] = IO_VSDEF(io,2) - 1 + + # L(i) :== LTM=(1 / block) * P(i) + Vx + # At pixel {P(i) :== (block + 1) / 2} L(i) is 1.0. + # Solve for Vx :== 1.0 - (1 / block) * ((block + 1) / 2) + # --> 0.5 - 1 / (2 * block) + + ltv_2[1] = 0.5d0 - 1.0d0 / double (max (1.0, IO_XBLOCK(io))) / 2.0d0 + ltv_2[2] = 0.5d0 - 1.0d0 / double (max (1.0, IO_YBLOCK(io))) / 2.0d0 + + do j = 1, ndim + do i = 1, ndim + if (i == j) { + if (i == 1) + ltm[i,j] = 1.0D0 / max (1.0, IO_XBLOCK(io)) + else + ltm[i,j] = 1.0D0 / max (1.0, IO_YBLOCK(io)) + } else + ltm[i,j] = 0 + + # Apply the transformation. + call mw_translated (mw, ltv_1, ltm, ltv_2, ndim) + + return (mw) +end diff --git a/sys/qpoe/qpiomkidx.x b/sys/qpoe/qpiomkidx.x new file mode 100644 index 00000000..979b1142 --- /dev/null +++ b/sys/qpoe/qpiomkidx.x @@ -0,0 +1,299 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <error.h> +include <mach.h> +include <fset.h> +include "qpoe.h" +include "qpio.h" + +define SZ_CODE 7 + + +# QPIO_MKINDEX -- Make an index for the event list associated with the QPIO +# descriptor. The event list must have been already written out, in sorted +# order according to the given key. Once an event list is indexed it cannot +# be further extended or otherwise modified. The key fields are specified +# as, e.g., "s10,s8" or "(s10,s8)" (Y // X), where the field name is the +# datatype code (silrd for short, int, long, real, or double) followed by the +# decimal byte offset of the field in the event struct. + +procedure qpio_mkindex (io, key) + +pointer io #I QPIO descriptor +char key[ARB] #I list of key fields + +pointer sp, tokbuf, ip, in, ev, ev_p, ov, lv, oo, bp +int ox, line, nevents, szs_event, ncols, nlines, nout, x, y, i, ch +int token, offset, xoff, yoff, len_index, nev, fd, sv_evi, firstev +int dtype, ntype + +long note() +pointer qp_opentext() +int qp_gettok(), ctoi(), qpio_rbucket(), pl_p2li(), sizeof() +errchk qp_opentext, qpio_rbucket, qpio_sync, write, calloc, syserrs +define nosort_ 91 + +begin + call smark (sp) + call salloc (tokbuf, SZ_TOKBUF, TY_CHAR) + call malloc (oo, IO_NLINES(io) * 3 + 32, TY_SHORT) + + ncols = IO_NCOLS(io) + nlines = IO_NLINES(io) + sv_evi = IO_EVI(io) + + # Key defaults to sort x/y. + xoff = IO_EVXOFF(io) + yoff = IO_EVYOFF(io) + dtype = IO_EVXTYPE(io) + + # Parse key list (macro references are permitted) to get offsets of + # the X and Y coordinate fields to be used as the index key. + + in = qp_opentext (IO_QP(io), key) + + do i = 1, 2 { + # Get next field token. + repeat { + token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) + } until (token == EOF || token == TOK_IDENTIFIER) + if (token == EOF) + break + + # Determine field type. + call strlwr (Memc[tokbuf]) + ch = Memc[tokbuf] + + switch (ch) { + case 's': + ntype = TY_SHORT + case 'i': + ntype = TY_INT + case 'l': + ntype = TY_LONG + case 'r': + ntype = TY_REAL + case 'd': + ntype = TY_DOUBLE + default: + call syserrs (SYS_QPXYFNS, key) + } + + # Both X and Y must be the same type. + if (i == 1) + dtype = ntype + else if (ntype != dtype) + call syserrs (SYS_QPINVEVT, key) + + ip = tokbuf + 1 + if (ctoi (Memc, ip, offset) <= 0) + call syserrs (SYS_QPBADKEY, key) + else + offset = offset / (sizeof(dtype) * SZB_CHAR) + + if (i == 1) + yoff = offset + else + xoff = offset + + while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF) + if (Memc[tokbuf] == ',') + break + } + + call qp_closetext (in) + + # Sync the event list to ensure that the bucket is flushed. + call qpio_sync (io) + + fd = IO_FD(io) + bp = IO_BP(io) + len_index = nlines + szs_event = IO_EVENTLEN(io) + + if (IO_DEBUG(io) > 1) { + call eprintf ("qpio_mkindex (%xX, `%s')\n") + call pargi (io) + call pargstr (key) + call eprintf ("nevents=%d, evsize=%d, xkey=%c%d, ykey=%c%d\n") + call pargi (IO_NEVENTS(io)) + call pargi (szs_event) + call pargi (ch) + call pargi (xoff) + call pargi (ch) + call pargi (yoff) + } + + # Allocate the offset and length vectors (comprising the index). + # These are deallocated at qpio_close time. + + call calloc (ov, len_index, TY_INT) + call calloc (lv, len_index, TY_INT) + + ox = -1 + line = 1 + firstev = 1 + nevents = 0 + + # Rewind the list. + i = qpio_rbucket (io, 1) + + # For each event in the event list... + for (IO_EVI(io)=1; IO_EVI(io) <= IO_NEVENTS(io); ) { + # Refill the event bucket? + if (IO_EVI(io) > IO_BKLASTEV(io)) + if (qpio_rbucket (io, IO_EVI(io)) == EOF) + break + + # Process all events in the bucket. + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + nev = min (IO_NEVENTS(io), IO_BKLASTEV(io)) - IO_EVI(io) + 1 + nout = 0 + + do i = 1, nev { + ev_p = (ev - 1) * SZ_SHORT / sizeof(dtype) + 1 + + switch (dtype) { + case TY_SHORT: + x = Mems[ev_p+xoff] + y = Mems[ev_p+yoff] + case TY_INT: + x = Memi[ev_p+xoff] + y = Memi[ev_p+yoff] + case TY_LONG: + x = Meml[ev_p+xoff] + y = Meml[ev_p+yoff] + case TY_REAL: + x = Memr[ev_p+xoff] + 0.5 + y = Memr[ev_p+yoff] + 0.5 + case TY_DOUBLE: + x = Memd[ev_p+xoff] + 0.5 + y = Memd[ev_p+yoff] + 0.5 + } + + x = max(1, min(ncols, x)) + y = max(1, min(nlines, y)) + + if (IO_DEBUG(io) > 4) { + # Egads! Dump every photon. + call eprintf ("(%04d,%04d) ") + call pargi (x) + call pargi (y) + nout = nout + 1 + if (nout >= 6) { + call eprintf ("\n") + nout = 0 + } + } + + if (y > line) { + # Add index entry. + if (nevents > 0) { + Memi[ov+line-1] = firstev + Memi[lv+line-1] = nevents + + if (IO_DEBUG(io) > 3 && nevents > 0) { + if (nout > 0) { + call eprintf ("\n") + nout = 0 + } + call eprintf ("%4d: ev=%d, nev=%d\n") + call pargi (line) + call pargi (firstev) + call pargi (nevents) + } + } + + # Set up the new line. + firstev = IO_EVI(io) + i - 1 + nevents = 1 + line = y + ox = x + + } else if (y == line) { + # Add another event to the current line. + nevents = nevents + 1 + if (x < ox) + goto nosort_ + else + ox = x + } else + goto nosort_ + + ev = ev + szs_event + } + + IO_EVI(io) = IO_EVI(io) + nev + if (nout > 0) { + call eprintf ("\n") + nout = 0 + } + } + + # Output final index entry. + if (nevents > 0) { + Memi[ov+line-1] = firstev + Memi[lv+line-1] = nevents + } + + # Apply data compression to the index arrays and append to the event + # list lfile. + + call fseti (fd, F_BUFSIZE, len_index * SZ_INT) + call seek (fd, EOF) + + IO_YOFFVOFF(io) = note (fd) + IO_YOFFVLEN(io) = pl_p2li (Memi[ov], 1, Mems[oo], len_index) + call write (fd, Mems[oo], IO_YOFFVLEN(io) * SZ_SHORT) + + IO_YLENVOFF(io) = note (fd) + IO_YLENVLEN(io) = pl_p2li (Memi[lv], 1, Mems[oo], len_index) + call write (fd, Mems[oo], IO_YLENVLEN(io) * SZ_SHORT) + + call flush (fd) + call fseti (fd, F_BUFSIZE, 0) + + # Update the remaining index related fields of the QPIO descriptor. + IO_INDEXLEN(io) = len_index + IO_YOFFVP(io) = ov + IO_YLENVP(io) = lv + + IO_IXXOFF(io) = xoff + IO_IXYOFF(io) = yoff + IO_IXXTYPE(io) = dtype + IO_IXYTYPE(io) = dtype + + if (IO_DEBUG(io) > 1) { + call eprintf ("index.offv %d words at offset %d\n") + call pargi (IO_YOFFVLEN(io)) + call pargi (IO_YOFFVOFF(io)) + call eprintf ("index.lenv %d words at offset %d\n") + call pargi (IO_YLENVLEN(io)) + call pargi (IO_YLENVOFF(io)) + } + + # Update the event list header. + call qpio_sync (io) + + IO_EVI(io) = sv_evi + call sfree (sp) + return + +nosort_ + # A nonsorted event list has been detected, hence we cannot build + # an index, but we need not abort since nonindexed event lists are + # still usable. + + if (nout > 0) + call eprintf ("\n") + iferr (call syserrs (SYS_QPEVNSORT, Memc[IO_PARAM(io)])) + call erract (EA_WARN) + + IO_INDEXLEN(io) = 0 + call mfree (ov, TY_INT) + call mfree (lv, TY_INT) + + IO_EVI(io) = sv_evi + call sfree (sp) +end diff --git a/sys/qpoe/qpioopen.x b/sys/qpoe/qpioopen.x new file mode 100644 index 00000000..4ed8a711 --- /dev/null +++ b/sys/qpoe/qpioopen.x @@ -0,0 +1,392 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <error.h> +include <mach.h> +include <fset.h> +include <plset.h> +include "qpoe.h" +include "qpex.h" +include "qpio.h" + +# QPIO_OPEN -- Open the named event list parameter for event i/o. Since event +# lists can only be read and written sequentially, there are only two useful +# i/o modes, namely, READ_ONLY and NEW_FILE. Filtering is permitted only +# when reading an event list; when writing to a new event list, the events +# are merely copied out as they are received. + +pointer procedure qpio_open (qp, paramex, mode) + +pointer qp #I QPOE descriptor +char paramex[ARB] #I event-list parameter plus expression list +int mode #I access mode + +bool newlist +pointer sp, io, dd, eh, op, oo, flist, deffilt, defmask, maskname +pointer param, expr, filter, filter_text, mask, umask, psym, dsym, name +int sz_filter, szb_page, nwords, nchars, junk, fd, ip, i, j + +pointer qp_gpsym(), qpex_open(), stname(), strefstab() +int qp_popen(), qp_lenf(), read(), pl_l2pi(), fstati() +int qp_geti(), qp_gstr(), qp_parsefl(), qpio_parse(), qpex_modfilter() + +errchk qp_bind, qp_geti, qpio_parse, qp_gpsym, qp_addf, qp_gstr +errchk qp_parsefl, qp_popen, qpex_open, qpio_loadmask, qpex_modfilter +errchk stname, calloc, malloc, realloc, read, syserrs +string s_deffilt DEF_FILTER +string s_defmask DEF_MASK +define done_ 91 + +begin + call smark (sp) + call salloc (deffilt, SZ_FNAME, TY_CHAR) + call salloc (defmask, SZ_FNAME, TY_CHAR) + call salloc (maskname, SZ_FNAME, TY_CHAR) + call salloc (umask, SZ_FNAME, TY_CHAR) + + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + newlist = (mode == NEW_FILE || mode == APPEND) + + if (QP_DEBUG(qp) > 0) { + call eprintf ("qpio_open (%xX, `%s', %d)\n") + call pargi (qp) + call pargstr (paramex) + call pargi (mode) + } + + # Allocate and initialize the QPIO descriptor. + call calloc (io, LEN_IODES, TY_STRUCT) + + call calloc (IO_DD(io), LEN_DDDES, TY_STRUCT) + call calloc (IO_PARAM(io), SZ_FNAME, TY_CHAR) + call calloc (IO_MASK(io), SZ_FNAME, TY_CHAR) + + IO_QP(io) = qp + IO_MODE(io) = mode + IO_DEBUG(io) = QP_DEBUG(qp) + IO_XBLOCK(io) = QP_XBLOCK(qp) + IO_YBLOCK(io) = QP_YBLOCK(qp) + IO_NODEFFILT(io) = QP_NODEFFILT(qp) + IO_NODEFMASK(io) = QP_NODEFMASK(qp) + IO_OPTBUFSIZE(io) = QP_OPTBUFSIZE(qp) + IO_ACTIVE(io) = NO + + dd = IO_DD(io) + param = IO_PARAM(io) + mask = IO_MASK(io) + filter = NULL + +iferr { + # Get the image dimensions. + IO_NCOLS(io) = qp_geti (qp, "axlen[1]") + IO_NLINES(io) = qp_geti (qp, "axlen[2]") + + # Parse the parameter expression into the parameter name and + # expression qualifier fields. Possible variations on the input + # syntax are "" (null string, default everything), "param" (parameter + # name only), "param[expr]" (parameter name plus expression), and + # "[expr]" or "expr" (expression only), where the parameter name may + # be specified as in "[param=value,...]", i.e., as a term in the + # expression (allowing it to be input by the user to override the + # default). + + op = param + for (ip=1; paramex[ip] != EOS && paramex[ip] != '['; ip=ip+1) { + Memc[op] = paramex[ip] + op = op + 1 + } + expr = ip + + # Parse the expression qualifier field to set the i/o parameters, + # e.g., region mask, event attribute filter, blocking factor, + # coordinate system, etc. All QPIO parameters are removed, returning + # the filter expression (if any) to be passed on to QPEX for event + # attribute filtering. The `filter' buffer is passed by pointer so + # that it may be reallocated if more space is needed. + + sz_filter = DEF_SZEXPRBUF + call malloc (filter, sz_filter, TY_CHAR) + if (qpio_parse (io, paramex[expr], + filter, sz_filter, Memc[mask], SZ_FNAME) == ERR) + call eprintf ("QPIO warning: error parsing options expression\n") + + # If no event list parameter was named, use the default. + if (Memc[param] == EOS) + call strcpy (DEF_EVENTPARAM, Memc[param], SZ_FNAME) + + # Verify the parameter's type if it already exists, or create a new + # parameter of the default type if the mode is newfile or append. + + psym = qp_gpsym (qp, Memc[param]) + if (psym != NULL) { + if (S_DTYPE(psym) != TY_USER || S_DSYM(psym) == NULL) + call syserrs (SYS_QPNEVPAR, Memc[param]) + else if (newlist && S_NELEM(psym) > 0) + call syserrs (SYS_QPCLOBBER, Memc[param]) + } else if (mode == READ_ONLY) { + call syserrs (SYS_QPUKNPAR, Memc[param]) + } else { + call qp_addf (qp, Memc[param], DEF_EVENTTYPE, 0, "", 0) + psym = qp_gpsym (qp, Memc[param]) + } + + # Get the field list for the user defined event structure. This + # defines the size of an event struct, lists the offset and type + # of each field, and indicates which fields are to be used for X + # and Y in positional accesses (unless already set in the paramex). + + dsym = strefstab (QP_ST(qp), S_DSYM(psym)) + nchars = S_NELEM(dsym) + name = stname (QP_ST(qp), dsym) + + call salloc (flist, nchars, TY_CHAR) + if (qp_gstr (qp, Memc[name], Memc[flist], nchars) < nchars) + call syserrs (SYS_QPBADVAL, Memc[name]) + + if (qp_parsefl (qp, Memc[flist], IO_DD(io)) <= 0) + call syserrs (SYS_QPINVDD, Memc[name]) + else if (IO_EVXOFF(io) == NULL && IO_EVYOFF(io) == NULL) { + i = DD_XFIELD(dd) + j = DD_YFIELD(dd) + if (i == 0 || j == 0) + call syserrs (SYS_QPNOXYF, Memc[name]) + + switch (DD_FTYPE(dd,i)) { + case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE: + IO_EVXTYPE(io) = DD_FTYPE(dd,i) + default: + call syserrs (SYS_QPXYFNS, Memc[name]) + } + + switch (DD_FTYPE(dd,j)) { + case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE: + IO_EVYTYPE(io) = DD_FTYPE(dd,j) + default: + call syserrs (SYS_QPXYFNS, Memc[name]) + } + + IO_EVXOFF(io) = DD_FOFFSET(dd,i) + IO_EVYOFF(io) = DD_FOFFSET(dd,j) + } + + IO_EVENTLEN(io) = DD_STRUCTLEN(dd) * SZ_STRUCT / SZ_SHORT + + # Open the lfile used to store the event list. + IO_FD(io) = qp_popen (qp, Memc[param], mode, BINARY_FILE) + IO_LF(io) = S_LFILE(psym) + IO_CHAN(io) = fstati (IO_FD(io), F_CHANNEL) + IO_PSYM(io) = psym + + # The rest of the initialization is performed in the first call to + # qpio_putev if we are writing a new event list. + + if (newlist) # EXIT if new event list + goto done_ # ----------------------- + + fd = IO_FD(io) + szb_page = QP_FMPAGESIZE(qp) + nchars = szb_page / SZB_CHAR + call salloc (eh, szb_page / (SZ_STRUCT*SZB_CHAR), TY_STRUCT) + call aclri (Memi[eh], szb_page / (SZ_STRUCT*SZB_CHAR)) + + # Read event list header. + if (read (fd, Memi[eh], nchars) < nchars) + call syserrs (SYS_QPNOEH, Memc[param]) + + IO_NEVENTS(io) = EH_NEVENTS(eh) + IO_EVENTLEN(io) = EH_EVENTLEN(eh) + IO_SZBBUCKET(io)= EH_SZBBUCKET(eh) + IO_BUCKETLEN(io)= EH_BUCKETLEN(eh) + IO_FBOFF(io) = EH_FBOFF(eh) + IO_EVMINOFF(io) = EH_EVMINOFF(eh) + IO_EVMAXOFF(io) = EH_EVMAXOFF(eh) + IO_INDEXLEN(io) = EH_INDEXLEN(eh) + IO_YOFFVOFF(io) = EH_YOFFVOFF(eh) + IO_YOFFVLEN(io) = EH_YOFFVLEN(eh) + IO_YLENVOFF(io) = EH_YLENVOFF(eh) + IO_YLENVLEN(io) = EH_YLENVLEN(eh) + IO_IXXOFF(io) = EH_IXXOFF(eh) + IO_IXYOFF(io) = EH_IXYOFF(eh) + IO_IXXTYPE(io) = EH_IXXTYPE(eh) + IO_IXYTYPE(io) = EH_IXYTYPE(eh) + + # Copy the MINEVL event struct into the QPIO descriptor. + nwords = IO_EVENTLEN(io) + call malloc (IO_MINEVL(io), nwords, TY_SHORT) + call amovs (Memi[eh+EH_MINEVLOFF(eh)], Mems[IO_MINEVL(io)], + IO_EVENTLEN(io)) + + # Copy the MAXEVL event struct into the QPIO descriptor. + call malloc (IO_MAXEVL(io), nwords, TY_SHORT) + call amovs (Memi[eh+EH_MAXEVLOFF(eh)], Mems[IO_MAXEVL(io)], + IO_EVENTLEN(io)) + + if (IO_DEBUG(io) > 0) { + call eprintf ("%s: nev=%d, szbk=%d, bklen=%d+2, ixlen=%d\n") + call pargstr (Memc[param]) + call pargi (IO_NEVENTS(io)) + call pargi (IO_SZBBUCKET(io)) + call pargi (IO_BUCKETLEN(io)) + call pargi (IO_INDEXLEN(io)) + } + + # Get compressed event list index, if any. + if (IO_INDEXLEN(io) > 0) { + call salloc (oo, IO_INDEXLEN(io) * 2, TY_SHORT) + call malloc (IO_YOFFVP(io), IO_INDEXLEN(io), TY_INT) + call malloc (IO_YLENVP(io), IO_INDEXLEN(io), TY_INT) + + nchars = IO_YOFFVLEN(io) * SZ_SHORT + call seek (fd, IO_YOFFVOFF(io)) + if (read (fd, Mems[oo], nchars) < nchars) + call syserrs (SYS_QPBADIX, Memc[param]) + junk = pl_l2pi (Mems[oo], 1, Memi[IO_YOFFVP(io)], IO_INDEXLEN(io)) + + nchars = IO_YLENVLEN(io) * SZ_SHORT + call seek (fd, IO_YLENVOFF(io)) + if (read (fd, Mems[oo], nchars) < nchars) + call syserrs (SYS_QPBADIX, Memc[param]) + junk = pl_l2pi (Mems[oo], 1, Memi[IO_YLENVP(io)], IO_INDEXLEN(io)) + } + + # We won't need the file buffer any more, so free it. + call fseti (fd, F_BUFSIZE, 0) + + # Compile the event attribute filter (EAF). Always open the default + # filter if one is provided with the datafile. If the user has also + # specified a filter, this will modify the default filter. + + if (IO_NODEFFILT(io) != YES) { + # Check for "deffilt.<evl>" first, then "deffilt". + call sprintf (Memc[deffilt], SZ_FNAME, "%s.%s") + call pargstr (s_deffilt) + call pargstr (Memc[param]) + nchars = qp_lenf (qp, Memc[deffilt]) + if (nchars <= 0) { + call strcpy (s_deffilt, Memc[deffilt], SZ_FNAME) + nchars = qp_lenf (qp, Memc[deffilt]) + } + + # Open the default filter if one was found. + if (nchars > 0) { + call salloc (filter_text, nchars, TY_CHAR) + if (qp_gstr(qp,Memc[deffilt],Memc[filter_text],nchars) < nchars) + call syserrs (SYS_QPBADVAL, Memc[deffilt]) + IO_EX(io) = qpex_open (qp, Memc[filter_text]) + IO_EXCLOSE(io) = YES + } + } + + # Fold in the user specified filter if one was given. + if (Memc[filter] != EOS) { + if (IO_EX(io) != NULL) { + if (qpex_modfilter (IO_EX(io), Memc[filter]) == ERR) + call fprintf (STDERR, + "Warning: error compiling QPIO filter\n") + } else { + IO_EX(io) = qpex_open (qp, Memc[filter]) + IO_EXCLOSE(io) = YES + } + + if (IO_DEBUG(io) > 0) { + call eprintf ("event attribute filter: %s\n") + call pargstr (Memc[filter]) + } + } + + # Open the region mask. This may be specified (named) in the parameter + # expression, else we try to open a default mask. If a mask is named, + # the name may be the name of a header parameter containing the mask + # as the stored array value (TY_OPAQUE parameter), the name of a header + # parameter containing the name of the mask (TY_CHAR), or the name of + # a mask storage file (.pl extension). + + # Make a copy of the user mask name, as qpio_loadmask will clobber it. + call strcpy (Memc[mask], Memc[umask], SZ_FNAME) + + if (IO_NODEFMASK(io) != YES) { + # Check for "defmask.<evl>" first, then "defmask". + call sprintf (Memc[defmask], SZ_FNAME, "%s.%s") + call pargstr (s_defmask) + call pargstr (Memc[param]) + nchars = qp_lenf (qp, Memc[defmask]) + if (nchars <= 0) { + call strcpy (s_defmask, Memc[defmask], SZ_FNAME) + nchars = qp_lenf (qp, Memc[defmask]) + } + + if (nchars > 0) + if (qp_gstr (qp, Memc[defmask], Memc[maskname], SZ_FNAME) > 0) + call qpio_loadmask (io, Memc[maskname], NO) + } + + # Load user specified mask. + if (Memc[umask] != EOS) + call qpio_loadmask (io, Memc[umask], YES) + else if (IO_INDEXLEN(io) > 0) + call malloc (IO_RL(io), RL_LENELEM*2, TY_INT) + + # Allocate the bucket buffer. + call malloc (IO_BP(io), IO_SZBBUCKET(io)/SZB_CHAR/SZ_SHORT, TY_SHORT) +done_ + # If no default rect was specified, set default bounding box for + # reading to be the entire image. + + if (IO_BBUSED(io) == NO) { + IO_VSDEF(io,1) = 1; IO_VSDEF(io,2) = 1 + IO_VEDEF(io,1) = IO_NCOLS(io); IO_VEDEF(io,2) = IO_NLINES(io) + } + + # Initialize the active BB to the default. + call amovi (IO_VSDEF(io,1), IO_VS(io,1), NDIM) + call amovi (IO_VEDEF(io,1), IO_VE(io,1), NDIM) + +} then { + # We branch here if any nasty errors occur above. Cleanup and free + # the partially opened descriptor and pass the error on to whoever + # called us. + + if (IO_BP(io) != NULL) + call mfree (IO_BP(io), TY_SHORT) + if (IO_RL(io) != NULL) + call mfree (IO_RL(io), TY_INT) + if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES) + call pl_close (IO_PL(io)) + if (IO_EX(io) != NULL) + call qpex_close (IO_EX(io)) + + if (IO_YLENVP(io) != NULL) + call mfree (IO_YLENVP(io), TY_INT) + if (IO_YOFFVP(io) != NULL) + call mfree (IO_YOFFVP(io), TY_INT) + if (IO_MINEVL(io) != NULL) + call mfree (IO_MINEVL(io), TY_SHORT) + if (IO_MAXEVL(io) != NULL) + call mfree (IO_MAXEVL(io), TY_SHORT) + if (IO_FD(io) != NULL) + call close (IO_FD(io)) + + if (IO_MASK(io) != NULL) + call mfree (IO_MASK(io), TY_CHAR) + if (IO_PARAM(io) != NULL) + call mfree (IO_PARAM(io), TY_CHAR) + if (IO_DD(io) != NULL) + call mfree (IO_DD(io), TY_STRUCT) + + if (filter != NULL) + call mfree (filter, TY_CHAR) + if (io != NULL) + call mfree (io, TY_STRUCT) + + call erract (EA_ERROR) +} + + # The filter can be regenerated, so don't keep the input expr around. + call mfree (filter, TY_CHAR) + + # Normal exit for read-only access. + call sfree (sp) + return (io) +end diff --git a/sys/qpoe/qpioparse.x b/sys/qpoe/qpioparse.x new file mode 100644 index 00000000..40f858ab --- /dev/null +++ b/sys/qpoe/qpioparse.x @@ -0,0 +1,374 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include <mach.h> +include "qpoe.h" +include "qpex.h" +include "qpio.h" + +# QPIO_PARSE -- Parse the QPIO expression operand input to qpio_open or +# qpio_setfilter. This consists of a comma delimited list of keyword=value +# terms. We factor out those which are QPIO related and deal with these +# directly, concatenating the remaining terms to be passed on to QPEX. +# The output filter buffer is resized as needed to hold the filter expr. +# ERR is returned as the function value if an error occurs while compiling +# the expression. + +int procedure qpio_parse (io, expr, filter, sz_filter, mask, sz_mask) + +pointer io #I QPIO descriptor +char expr[ARB] #I expression to be parsed +pointer filter #U filter buffer +int sz_filter #U allocated buffer size +char mask[sz_mask] #O new mask name (not reallocatable) +int sz_mask #I max chars out + +real rval +pointer qp, sp, keyword, vp, in +int assignop, byte_offset, sz_field +int level, zlevel, status, start, value, token, op, kw, tokno + +pointer qp_opentext() +int qp_gettok(), gstrcpy(), strlen(), strdic(), ctoi(), ctor() +errchk qp_opentext, malloc, realloc, qp_gettok, qp_ungettok, syserrs + +define F Memc[filter+($1)-1] +define noval_ 91 +define badval_ 92 +define badkey_ 93 + +begin + call smark (sp) + call salloc (keyword, SZ_FNAME, TY_CHAR) + + qp = IO_QP(io) + + # Open the input expression for macro expanded token input. + in = qp_opentext (qp, expr) + + # Extract and process a series of "param[=expr]" terms, where + # the expr may be any series of tokens, delimited by an + # unparenthesized comma. + + op = 1 + tokno = 0 + F(op) = EOS + mask[1] = EOS + status = OK + level = 0 + + repeat { + start = op + + # Advance to the next keyword. + token = qp_gettok (in, F(op), SZ_TOKBUF) + tokno = tokno + 1 + + switch (token) { + case EOF: + break + case '(', '[', '{': + level = level + 1 + next + case ')', ']', '}': + level = level - 1 + next + case '!': + if (tokno <= 2) { + IO_NODEFFILT(io) = YES + IO_NODEFMASK(io) = YES + tokno = 1 + } + next + case TOK_IDENTIFIER: + op = op + strlen (F(op)) + if (op + SZ_TOKBUF > sz_filter) { + sz_filter = sz_filter + INC_SZEXPRBUF + call realloc (filter, sz_filter, TY_CHAR) + } + call strcpy (F(start), Memc[keyword], SZ_FNAME) + call strlwr (Memc[keyword]) + default: + if (token != ',') { + call eprintf ("QPIO: unexpected token `%s'\n") + call pargstr (F(op)) + status = ERR + } + next + } + + value = NULL + token = qp_gettok (in, F(op), SZ_TOKBUF) + + if (token == '=' || + token == TOK_PLUSEQUALS || token == TOK_COLONEQUALS) { + + # Accumulate the expression. + zlevel = level + assignop = token + op = op + strlen (F(op)) + value = op + + repeat { + # Peek at the next token to see if it terminates the + # expression. An unparenthesized comma or unmatched + # right brace, bracket, or parenthesis is part of the + # next statement and terminates the expression. + + token = qp_gettok (in, F(op), SZ_TOKBUF) + switch (token) { + case EOF: + break + case '(', '[', '{': + level = level + 1 + case ')', ']', '}': + if (level <= zlevel) { + call qp_ungettok (in, F(op)) + F(op) = EOS + break + } else + level = level - 1 + case ',': + if (level <= zlevel) { + call qp_ungettok (in, F(op)) + F(op) = EOS + break + } + } + + # Accept token as data. + op = op + strlen (F(op)) + if (op + SZ_TOKBUF + 1 > sz_filter) { + sz_filter = sz_filter + INC_SZEXPRBUF + call realloc (filter, sz_filter, TY_CHAR) + } + + F(op) = ' '; op = op + 1 + F(op) = EOS + } + } + + # Process the keywords known to QPIO and pass anything else on + # to the output filter buffer. + + kw = strdic (Memc[keyword], Memc[keyword], SZ_FNAME, KEYWORDS) + vp = filter + value - 1 + + switch (kw) { + case KW_BLOCK: + # Set the XY blocking factor for pixelation. + if (value == NULL) + goto noval_ + else if (ctor (Memc, vp, rval) <= 0) + goto badval_ + IO_XBLOCK(io) = rval + IO_YBLOCK(io) = rval + op = start + + case KW_XBLOCK: + # Set the X blocking factor for pixelation. + if (value == NULL) + goto noval_ + else if (ctor (Memc, vp, rval) <= 0) + goto badval_ + IO_XBLOCK(io) = rval + op = start + + case KW_YBLOCK: + # Set the Y blocking factor for pixelation. + if (value == NULL) + goto noval_ + else if (ctor (Memc, vp, rval) <= 0) + goto badval_ + IO_YBLOCK(io) = rval + op = start + + case KW_DEBUG: + # Set the debug level, default 1 if no argument. + if (value == NULL) + IO_DEBUG(io) = 1 + else if (ctoi (Memc, vp, IO_DEBUG(io)) <= 0) { + IO_DEBUG(io) = QP_DEBUG(qp) +badval_ call eprintf ("QPIO: cannot convert `%s' to integer\n") + call pargstr (Memc[vp]) + } + op = start + + case KW_FILTER: + # A term such as "filter=(...)". Keep the (...). + if (value == NULL) + goto noval_ + else { + # Accumulate expression term. + op = start + gstrcpy (Memc[vp], F(start), ARB) + F(op) = ','; op = op + 1 + F(op) = EOS + } + + case KW_KEY: + # Set the offsets of the event attribute fields to be used + # for the event coordinates during extraction. The typical + # syntax of the key value is, e.g., key=(s10,s8). Fields + # used for event coordinate keys must be a numeric type. + + call strlwr (Memc[vp]) + while (Memc[vp] == ' ' || Memc[vp] == '(') + vp = vp + 1 + + # Get the X field offset and type. + switch (Memc[vp]) { + case 's': + IO_EVXTYPE(io) = TY_SHORT + sz_field = SZ_SHORT + case 'i': + IO_EVXTYPE(io) = TY_INT + sz_field = SZ_INT + case 'l': + IO_EVXTYPE(io) = TY_LONG + sz_field = SZ_LONG + case 'r': + IO_EVXTYPE(io) = TY_REAL + sz_field = SZ_REAL + case 'd': + IO_EVXTYPE(io) = TY_DOUBLE + sz_field = SZ_DOUBLE + default: + goto badkey_ + } + + vp = vp + 1 + if (ctoi (Memc, vp, byte_offset) <= 0) + goto badkey_ + else + IO_EVXOFF(io) = byte_offset / (sz_field * SZB_CHAR) + + while (Memc[vp] == ' ' || Memc[vp] == ',') + vp = vp + 1 + + # Get the Y field offset. + switch (Memc[vp]) { + case 's': + IO_EVYTYPE(io) = TY_SHORT + sz_field = SZ_SHORT + case 'i': + IO_EVYTYPE(io) = TY_INT + sz_field = SZ_INT + case 'l': + IO_EVYTYPE(io) = TY_LONG + sz_field = SZ_LONG + case 'r': + IO_EVYTYPE(io) = TY_REAL + sz_field = SZ_REAL + case 'd': + IO_EVYTYPE(io) = TY_DOUBLE + sz_field = SZ_DOUBLE + default: + goto badkey_ + } + + vp = vp + 1 + if (ctoi (Memc, vp, byte_offset) <= 0) { +badkey_ call eprintf ("QPIO: bad key value `%s'\n") + call pargstr (F(value)) + status = ERR + } else + IO_EVYOFF(io) = byte_offset / (sz_field * SZB_CHAR) + + op = start + + case KW_NOINDEX: + # Disable use of the index for extraction (for testing). + IO_NOINDEX(io) = YES + op = start + + case KW_PARAM, KW_MASK: + # Set a string valued option. + + if (value == NULL) { +noval_ call eprintf ("QPIO: kewyord `%s' requires an argument\n") + call pargstr (Memc[keyword]) + status = ERR + + } else { + # Kill space added at end of token. + op = op - 1 + F(op) = EOS + + # Output the string. + if (kw == KW_PARAM) { + # Set the name of the event list parameter. + call strcpy (Memc[vp], Memc[IO_PARAM(io)], SZ_FNAME) + } else { + # Set the name of the region mask. + call strcpy (Memc[vp], mask, sz_mask) + if (assignop == TOK_COLONEQUALS) + IO_NODEFMASK(io) = YES + } + } + op = start + + case KW_RECT: + # Set the source rect or "bounding box" for i/o. The syntax + # is somewhat flexible, i.e., "*", ":N", "N:", "M:N" are + # all accepted ways of expressing the range for an axis. + + IO_VSDEF(io,1) = 1; IO_VSDEF(io,2) = 1 + IO_VEDEF(io,1) = IO_NCOLS(io); IO_VEDEF(io,2) = IO_NLINES(io) + + if (Memc[vp] == '[' || Memc[vp] == '(') # ]) + vp = vp + 1 + while (Memc[vp] == ' ') + vp = vp + 1 + + # Get range in X. + if (Memc[vp] == '*') + vp = vp + 1 + else { + if (ctoi (Memc, vp, IO_VSDEF(io,1)) <= 0) + IO_VSDEF(io,1) = 1 + while (IS_WHITE(Memc[vp]) || Memc[vp] == ':') + vp = vp + 1 + if (ctoi (Memc, vp, IO_VEDEF(io,1)) <= 0) + IO_VEDEF(io,1) = IO_NCOLS(io) + } + + while (IS_WHITE(Memc[vp]) || Memc[vp] == ',') + vp = vp + 1 + + # Get range in Y. + if (Memc[vp] == '*') + vp = vp + 1 + else { + if (ctoi (Memc, vp, IO_VSDEF(io,2)) <= 0) + IO_VSDEF(io,2) = 1 + while (IS_WHITE(Memc[vp]) || Memc[vp] == ':') + vp = vp + 1 + if (ctoi (Memc, vp, IO_VEDEF(io,2)) <= 0) + IO_VEDEF(io,2) = IO_NLINES(io) + } + + IO_BBUSED(io) = YES + op = start + + default: + # Accumulate EAF expression term. + F(op) = ','; op = op + 1 + F(op) = ' '; op = op + 1 + F(op) = EOS + } + } + + # Verify that the parens etc. match. + if (level != 0) + call syserrs (SYS_QPIOSYN, QP_DFNAME(qp)) + + F(op) = EOS + sz_filter = op + call realloc (filter, sz_filter, TY_CHAR) + + call qp_closetext (in) + call sfree (sp) + + return (status) +end diff --git a/sys/qpoe/qpioputev.x b/sys/qpoe/qpioputev.x new file mode 100644 index 00000000..69c6d26d --- /dev/null +++ b/sys/qpoe/qpioputev.x @@ -0,0 +1,104 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "qpoe.h" +include "qpio.h" + +# QPIO_PUTEVENTS -- Append events to a new event list. No filtering is +# performed. As events are received they are merely copied into the bucket +# currently being filled, writing each bucket to the output lfile as it fills. +# No sorting is performed, hence if an indexed list is desired, the caller +# must output the events in sort order (normally sorted by Y and then by X +# within each image line). + +procedure qpio_putevents (io, i_ev, nevents) + +pointer io #I QPIO descriptor +pointer i_ev[ARB] #I array of event pointers +int nevents #I number of events + +pointer qp, bp, ev +int szs_event, szb_page, nwords, bklen, bksiz, nev, i, j +errchk qpio_wbucket, qpio_sync, malloc, calloc + +begin + szs_event = IO_EVENTLEN(io) + bp = IO_BP(io) + + # Fix the event list parameters and write out the event list header + # when the first write to a new event list occurs. + + if (IO_ACTIVE(io) == NO) { + qp = IO_QP(io) + szb_page = QP_FMPAGESIZE(qp) + + IO_FBOFF(io) = szb_page + 1 + IO_EVENTLEN(io) = DD_STRUCTLEN(IO_DD(io))*SZ_STRUCT/SZ_SHORT + IO_NEVENTS(io) = 0 + + # Force the bucket size to an integral number of datafile pages, + # and adjust the number of events to fill the bucket, allowing + # 2 extra slots at the end for the min/max event structs. + + bklen = QP_BUCKETLEN(qp) + 2 + bksiz = bklen * (IO_EVENTLEN(io) * SZ_SHORT * SZB_CHAR) + bksiz = (bksiz - 1) / szb_page * szb_page + bklen = bksiz / (IO_EVENTLEN(io) * SZ_SHORT * SZB_CHAR) + + IO_BUCKETLEN(io) = bklen - 2 + IO_SZBBUCKET(io) = bksiz + IO_EVMINOFF(io) = szs_event * (bklen - 2) + IO_EVMAXOFF(io) = szs_event * (bklen - 1) + IO_EVI(io) = 1 + IO_BKNO(io) = 1 + IO_BKFIRSTEV(io) = 1 + IO_BKLASTEV(io) = bklen - 2 + + if (IO_DEBUG(io) > 1) { + call eprintf ("%s: assign szbk=%d, bklen=%d+2\n") + call pargstr (Memc[IO_PARAM(io)]) + call pargi (bksiz) + call pargi (bklen - 2) + } + + # Allocate the bucket buffer. + call malloc (IO_BP(io), bksiz / SZB_CHAR / SZ_SHORT, TY_SHORT) + bp = IO_BP(io) + + # Allocate the MINEVL and MAXEVL event structs, used to keep + # track of the min and max event field values for the entire + # event list. + + nwords = IO_EVENTLEN(io) + call calloc (IO_MINEVL(io), nwords, TY_SHORT) + call calloc (IO_MAXEVL(io), nwords, TY_SHORT) + + # Write the event list header. + call qpio_sync (io) + + IO_ACTIVE(io) = YES + } + + # Make sure there is room in the bucket. + if (IO_EVI(io) > IO_BKLASTEV(io)) + call qpio_wbucket (io, IO_EVI(io)) + + # Output the current batch of events. + for (j=0; j < nevents; j=j+nev) { + # Copy out as many events as will fit in the bucket. + nev = min (nevents-j, (IO_BKLASTEV(io) - IO_EVI(io) + 1)) + if (nev <= 0) + break + + ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event + do i = 1, nev { + call amovs (Mems[i_ev[i+j]], Mems[ev], szs_event) + ev = ev + szs_event + } + + # Write out the bucket if it fills. + IO_EVI(io) = IO_EVI(io) + nev + if (IO_EVI(io) > IO_BKLASTEV(io)) + call qpio_wbucket (io, IO_EVI(io)) + } +end diff --git a/sys/qpoe/qpiorb.x b/sys/qpoe/qpiorb.x new file mode 100644 index 00000000..9d46cc59 --- /dev/null +++ b/sys/qpoe/qpiorb.x @@ -0,0 +1,44 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpio.h" + +# QPIO_RBUCKET -- Load the bucket containing the specified event into the +# QPIO descriptor, returning EOF if the numbered event does not exist. + +int procedure qpio_rbucket (io, evi) + +pointer io #I QPIO descriptor +int evi #I bucket number desired + +int ev1, ev2, nb +int offset, bkno, status + +begin + # Event does not exist? + if (evi < 1 || evi > IO_NEVENTS(io)) + return (EOF) + + # Bucket already loaded? + bkno = EVI_TO_BUCKET(io,evi) + if (bkno == IO_BKNO(io)) + return (bkno) + + # Determine range of events in bucket. + ev1 = BUCKET_TO_EVI(io,bkno) + ev2 = min (IO_NEVENTS(io), ev1 + IO_BUCKETLEN(io) - 1) + + # Physically read the bucket. + nb = IO_SZBBUCKET(io) + offset = (bkno - 1) * nb + IO_FBOFF(io) + call fm_lfaread (IO_CHAN(io), Mems[IO_BP(io)], nb, offset) + call fm_lfawait (IO_CHAN(io), status) + if (status < nb) + return (EOF) + + # Update the bucket descriptor. + IO_BKNO(io) = bkno + IO_BKFIRSTEV(io) = ev1 + IO_BKLASTEV(io) = ev2 + + return (bkno) +end diff --git a/sys/qpoe/qpiorpix.gx b/sys/qpoe/qpiorpix.gx new file mode 100644 index 00000000..e766749c --- /dev/null +++ b/sys/qpoe/qpiorpix.gx @@ -0,0 +1,86 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <syserr.h> +include "../qpio.h" + +# QPIO_READPIX -- Sample the event list within the indicated rectangular +# region, using the given blocking factor, to produce a rectangular array +# of "pixels", where each pixel is a count of the number of events mapping +# to that location which pass the event attribute filter and region mask. +# +# NOTE -- It is left up to the caller to zero the output buffer before +# we are called. (We merely increment the counts of the affected pixels). + +int procedure qpio_readpix$t (io, obuf, vs, ve, ndim, xblock, yblock) + +pointer io #I QPIO descriptor +PIXEL obuf[ARB] #O output pixel buffer +int vs[ndim], ve[ndim] #I vectors defining region to be extracted +int ndim #I should be 2 for QPOE +real xblock, yblock #I blocking factors + +double x, y +pointer sp, evl, ev_p +int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j +errchk qpio_getevents, qpio_setrange, syserr +int qpio_getevents() + +begin + # Verify arguments. + if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1)) + call syserr (SYS_QPBLOCKOOR) + if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1)) + call syserr (SYS_QPBLOCKOOR) + + # Compute the size of the output matrix in integer pixels. This + # truncates the last partially filled pixel in each axis. + + xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000)) + yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000)) + if (xw <= 0 || yw <= 0) + return (0) + + call smark (sp) + call salloc (evl, SZ_EVLIST, TY_POINTER) + + xoff = IO_EVXOFF(io) + yoff = IO_EVYOFF(io) + maxpix = xw * yw + totev = 0 + + evtype = IO_EVXTYPE(io) + if (IO_EVXTYPE(io) != IO_EVYTYPE(io)) + call syserr (SYS_QPINVEVT) + + # Define the region from which we wish to read events. + call qpio_setrange (io, vs, ve, ndim) + + # Read the events. + while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) { + switch (evtype) { + $for (silrd) + case TY_PIXEL: + # Process a sequence of neighbor events. + do i = 1, nev { + ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_PIXEL + 1 + + x = Mem$t[ev_p+xoff] + y = Mem$t[ev_p+yoff] + + j = int ((y - vs[2]) / yblock + (EPSILOND * 1000)) + if (j >= 0 && j < yw) { + pix = j * xw + (x - vs[1]) / xblock + 1 + if (pix > 0 && pix <= maxpix) + obuf[pix] = obuf[pix] + 1 + } + } + $endfor + } + + totev = totev + nev + } + + call sfree (sp) + return (totev) +end diff --git a/sys/qpoe/qpiosetfil.x b/sys/qpoe/qpiosetfil.x new file mode 100644 index 00000000..4f5f9833 --- /dev/null +++ b/sys/qpoe/qpiosetfil.x @@ -0,0 +1,59 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpex.h" +include "qpio.h" + +# QPIO_SETFILTER -- Modify the filter used to reject events during event +# extraction with qpio_getevents or qpio_readpix. Possible items to be set +# here include the event attribute filter, region mask, and various QPIO +# parameters. The input expression should be a comma delimited list of +# param=value terms, where PARAM is `filter', `mask', or the name of a QPIO +# or QPEX parameter, and where `value' is an expression, e.g., a comma +# delimited list of range terms enclosed in parenthesis. + +procedure qpio_setfilter (io, expr) + +pointer io #I QPIO descriptor +char expr[ARB] #I option setting expression + +int sz_filter +pointer sp, filter, mask +errchk qpio_parse, qpex_open, qpex_modfilter +int qpex_modfilter(), qpio_parse() +pointer qpex_open() + +begin + call smark (sp) + call salloc (mask, SZ_FNAME, TY_CHAR) + + if (IO_DEBUG(io) > 0) { + call eprintf ("qpio_setfilter (%xX, `%s')\n") + call pargi (io) + call pargstr (expr) + } + + # Parse full QPIO oriented filter expression. + sz_filter = DEF_SZEXPRBUF + call malloc (filter, sz_filter, TY_CHAR) + if (qpio_parse (io,expr,filter,sz_filter,Memc[mask],SZ_FNAME) == ERR) { + call eprintf ("QPIO warning: error parsing `%s'\n") + call pargstr (expr) + } + + # Set event attribute filter. + if (IO_EX(io) == NULL) + IO_EX(io) = qpex_open (IO_QP(io), Memc[filter]) + else if (qpex_modfilter (IO_EX(io), Memc[filter]) == ERR) { + call eprintf ("Warning: errors compiling `%s'\n") + call pargstr (expr) + } + + # Set region mask. + if (Memc[mask] != EOS) + call qpio_loadmask (io, Memc[mask], NO) + + IO_ACTIVE(io) = NO + + call mfree (filter, TY_CHAR) + call sfree (sp) +end diff --git a/sys/qpoe/qpioseti.x b/sys/qpoe/qpioseti.x new file mode 100644 index 00000000..d5dadceb --- /dev/null +++ b/sys/qpoe/qpioseti.x @@ -0,0 +1,90 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpioset.h> +include <syserr.h> +include <plset.h> +include "qpio.h" + +# QPIO_SETI -- Set a QPIO interface integer valued parameter. This procedure +# represents the lowest level interface by which an applications program can +# control QPIO. + +procedure qpio_seti (io, param, value) + +pointer io #I QPIO descriptor +int param #I parameter code +int value #I new parameter value + +int naxes, axlen[PL_MAXDIM], sv_active +errchk pl_close, syserr, realloc + +begin + # Almost everything here cancels any active i/o. + sv_active = IO_ACTIVE(io) + IO_ACTIVE(io) = NO + + # Set the named parameter. + switch (param) { + case QPIO_BLOCKFACTOR: + IO_XBLOCK(io) = value + IO_YBLOCK(io) = value + case QPIO_XBLOCKFACTOR: + IO_XBLOCK(io) = value + case QPIO_YBLOCKFACTOR: + IO_YBLOCK(io) = value + case QPIO_EVXOFF: + IO_EVXOFF(io) = value + case QPIO_EVYOFF: + IO_EVYOFF(io) = value + case QPIO_EVXTYPE: + IO_EVXTYPE(io) = value + case QPIO_EVYTYPE: + IO_EVYTYPE(io) = value + case QPIO_NOINDEX: + IO_NOINDEX(io) = value + case QPIO_NODEFFILT: + IO_NODEFFILT(io) = value + case QPIO_NODEFMASK: + IO_NODEFMASK(io) = value + case QPIO_OPTBUFSIZE: + IO_OPTBUFSIZE(io) = value + + case QPIO_BUCKETLEN: + # Set the bucket length (new event lists only). + if (IO_MODE(io) != READ_ONLY) + IO_BUCKETLEN(io) = value + + case QPIO_DEBUG: + # Set the debug level; don't modify IO_ACTIVE. + IO_ACTIVE(io) = sv_active + IO_DEBUG(io) = value + + case QPIO_EX: + # Set the event attribute filter. + if (IO_EX(io) != NULL && IO_EXCLOSE(io) == YES) + call qpex_close (IO_EX(io)) + IO_EX(io) = value + IO_EXCLOSE(io) = NO + + case QPIO_PL: + # Set the PLIO region mask. + if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES) + call pl_close (IO_PL(io)) + + IO_PL(io) = value + IO_PLCLOSE(io) = NO + call pl_gsize (IO_PL(io), naxes, axlen, IO_MDEPTH(io)) + if (axlen[1] != IO_NCOLS(io) || axlen[2] != IO_NLINES(io)) + call syserr (SYS_QPPLSIZE) + + # Allocate a range list buffer if i/o is indexed. + if (IO_INDEXLEN(io) > 0) + call realloc (IO_RL(io), RL_MAXLEN(IO_PL(io)), TY_INT) + + # Update the mask name, such as it is... + if (IO_MASK(io) != NULL) { + call sprintf (Memc[IO_MASK(io)], SZ_FNAME, "%xX") + call pargi (value) + } + } +end diff --git a/sys/qpoe/qpiosetr.x b/sys/qpoe/qpiosetr.x new file mode 100644 index 00000000..768e1b82 --- /dev/null +++ b/sys/qpoe/qpiosetr.x @@ -0,0 +1,30 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpioset.h> +include "qpio.h" + +# QPIO_SETR -- Set a QPIO interface real valued parameter. This procedure +# represents the lowest level interface by which an applications program can +# control QPIO. + +procedure qpio_setr (io, param, value) + +pointer io #I QPIO descriptor +int param #I parameter code +real value #I new parameter value + +begin + # Almost everything here cancels any active i/o. + IO_ACTIVE(io) = NO + + # Set the named parameter. + switch (param) { + case QPIO_BLOCKFACTOR: + IO_XBLOCK(io) = value + IO_YBLOCK(io) = value + case QPIO_XBLOCKFACTOR: + IO_XBLOCK(io) = value + case QPIO_YBLOCKFACTOR: + IO_YBLOCK(io) = value + } +end diff --git a/sys/qpoe/qpiosetrg.x b/sys/qpoe/qpiosetrg.x new file mode 100644 index 00000000..6a93f11e --- /dev/null +++ b/sys/qpoe/qpiosetrg.x @@ -0,0 +1,34 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpio.h" + +# QPIO_SETRANGE -- Set the range in X and Y within which events will be +# extracted by qpio_getevents. This defines the "bounding box" for i/o +# and "rewinds" the getevent i/o pointer. + +procedure qpio_setrange (io, vs, ve, ndim) + +pointer io #I QPIO descriptor +int vs[ARB] #I start vector (lower left corner) +int ve[ARB] #I end vector (upper right corner) +int ndim #I vector length (ndim=2 at present) + +int i +int vlim[NDIM] + +begin + vlim[1] = IO_NCOLS(io) + vlim[2] = IO_NLINES(io) + + if (ndim <= 0) { + call amovi (IO_VSDEF(io,1), IO_VS(io,1), NDIM) + call amovi (IO_VEDEF(io,1), IO_VE(io,1), NDIM) + } else { + do i = 1, ndim { + IO_VS(io,i) = max(1, min(vlim[i], vs[i])) + IO_VE(io,i) = max(1, min(vlim[i], ve[i])) + } + } + + IO_ACTIVE(io) = NO +end diff --git a/sys/qpoe/qpiostati.x b/sys/qpoe/qpiostati.x new file mode 100644 index 00000000..678ee0bb --- /dev/null +++ b/sys/qpoe/qpiostati.x @@ -0,0 +1,84 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpioset.h> +include "qpio.h" + +# QPIO_STATI -- Stat a QPIO interface integer valued parameter. + +int procedure qpio_stati (io, param) + +pointer io #I QPIO descriptor +int param #I parameter code + +bool fp_equalr() + +begin + switch (param) { + case QPIO_BLOCKFACTOR: + if (fp_equalr (IO_XBLOCK(io), IO_YBLOCK(io))) + return (IO_XBLOCK(io)) + else + return (ERR) + case QPIO_XBLOCKFACTOR: + return (IO_XBLOCK(io)) + case QPIO_YBLOCKFACTOR: + return (IO_YBLOCK(io)) + case QPIO_BUCKETLEN: + return (IO_BUCKETLEN(io)) + case QPIO_DEBUG: + return (IO_DEBUG(io)) + case QPIO_EVXOFF: + return (IO_EVXOFF(io)) + case QPIO_EVYOFF: + return (IO_EVYOFF(io)) + case QPIO_EVXTYPE: + return (IO_EVXTYPE(io)) + case QPIO_EVYTYPE: + return (IO_EVYTYPE(io)) + case QPIO_EX: + return (IO_EX(io)) + case QPIO_NODEFFILT: + return (IO_NODEFFILT(io)) + case QPIO_NODEFMASK: + return (IO_NODEFMASK(io)) + case QPIO_NOINDEX: + return (IO_NOINDEX(io)) + case QPIO_OPTBUFSIZE: + return (IO_OPTBUFSIZE(io)) + case QPIO_PL: + return (IO_PL(io)) + + case QPIO_EVENTLEN: # length of event struct, shorts + return (IO_EVENTLEN(io)) + case QPIO_FD: # FIO fd of event list lfile + return (IO_FD(io)) + case QPIO_INDEXLEN: # index length (0=noindex) + return (IO_INDEXLEN(io)) + case QPIO_IXXOFF: # offset of X in index + return (IO_IXXOFF(io)) + case QPIO_IXYOFF: # offset of Y in index + return (IO_IXYOFF(io)) + case QPIO_IXXTYPE: # datatype of X in index + return (IO_IXXTYPE(io)) + case QPIO_IXYTYPE: # datatype of Y in index + return (IO_IXYTYPE(io)) + case QPIO_LF: # FMIO lfile number + return (IO_LF(io)) + case QPIO_MASKP: # PLIO descriptor + return (IO_MASK(io)) + case QPIO_MAXEVP: # pointer to short + return (IO_MAXEVL(io)) + case QPIO_MINEVP: # pointer to short + return (IO_MINEVL(io)) + case QPIO_NCOLS: + return (IO_NCOLS(io)) + case QPIO_NLINES: + return (IO_NLINES(io)) + case QPIO_PARAMP: # pointer to char + return (IO_PARAM(io)) + case QPIO_QP: + return (IO_QP(io)) # QPOE descriptor + } + + return (ERR) +end diff --git a/sys/qpoe/qpiostatr.x b/sys/qpoe/qpiostatr.x new file mode 100644 index 00000000..429ea2e9 --- /dev/null +++ b/sys/qpoe/qpiostatr.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpioset.h> +include "qpio.h" + +# QPIO_STATR -- Stat a QPIO interface real valued parameter. + +real procedure qpio_statr (io, param) + +pointer io #I QPIO descriptor +int param #I parameter code + +bool fp_equalr() + +begin + switch (param) { + case QPIO_BLOCKFACTOR: + if (fp_equalr (IO_XBLOCK(io), IO_YBLOCK(io))) + return (IO_XBLOCK(io)) + else + return (ERR) + case QPIO_XBLOCKFACTOR: + return (IO_XBLOCK(io)) + case QPIO_YBLOCKFACTOR: + return (IO_YBLOCK(io)) + } + + return (ERR) +end diff --git a/sys/qpoe/qpiosync.x b/sys/qpoe/qpiosync.x new file mode 100644 index 00000000..35492d7d --- /dev/null +++ b/sys/qpoe/qpiosync.x @@ -0,0 +1,78 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <fset.h> +include "qpoe.h" +include "qpio.h" + +# QPIO_SYNC -- Update an event list on disk, i.e., flush the bucket buffer if +# it has been written into, and update the event list header. No QPIO state +# parameters are modified, e.g., the i/o pointer IO_EVI is not affected, +# nor are the contents of the bucket currently being filled; partially filled +# buckets can be synced if desired. + +procedure qpio_sync (io) + +pointer io #I QPIO descriptor + +pointer sp, eh +int szb_page, off, flen +int fstati() +errchk qpio_wbucket + +begin + if (IO_MODE(io) == READ_ONLY) + return + + # Flush the bucket buffer. + if (IO_EVI(io) > 1) + call qpio_wbucket (io, IO_EVI(io)) + + call smark (sp) + szb_page = QP_FMPAGESIZE(IO_QP(io)) + + # Update the event list header (stored in a full datafile page). + call salloc (eh, szb_page / (SZ_STRUCT*SZB_CHAR), TY_STRUCT) + call aclri (Memi[eh], szb_page / (SZ_STRUCT*SZB_CHAR)) + + EH_FBOFF(eh) = szb_page + 1 + EH_NEVENTS(eh) = IO_NEVENTS(io) + EH_EVENTLEN(eh) = IO_EVENTLEN(io) + EH_SZBBUCKET(eh) = IO_SZBBUCKET(io) + EH_BUCKETLEN(eh) = IO_BUCKETLEN(io) + EH_EVMINOFF(eh) = IO_EVMINOFF(io) + EH_EVMAXOFF(eh) = IO_EVMAXOFF(io) + EH_INDEXLEN(eh) = IO_INDEXLEN(io) + EH_YOFFVOFF(eh) = IO_YOFFVOFF(io) + EH_YOFFVLEN(eh) = IO_YOFFVLEN(io) + EH_YLENVOFF(eh) = IO_YLENVOFF(io) + EH_YLENVLEN(eh) = IO_YLENVLEN(io) + EH_IXXOFF(eh) = IO_IXXOFF(io) + EH_IXYOFF(eh) = IO_IXYOFF(io) + EH_IXXTYPE(eh) = IO_IXXTYPE(io) + EH_IXYTYPE(eh) = IO_IXYTYPE(io) + + # Output MINEV and MAXEV event structs following the header struct, + # but in the header page. + + if (IO_MINEVL(io) != NULL) { + off = LEN_EHDES + call amovs (Mems[IO_MINEVL(io)], Memi[eh+off], IO_EVENTLEN(io)) + EH_MINEVLOFF(eh) = off + } + + if (IO_MAXEVL(io) != NULL) { + off = LEN_EHDES + (IO_EVENTLEN(io) * SZ_SHORT / SZ_STRUCT) + call amovs (Mems[IO_MAXEVL(io)], Memi[eh+off], IO_EVENTLEN(io)) + EH_MAXEVLOFF(eh) = off + } + + # Write the header page to the lfile. + call fm_lfawrite (IO_CHAN(io), Memi[eh], szb_page, 1) + call fm_lfawait (IO_CHAN(io), szb_page) + flen = fstati (IO_FD(io), F_FILESIZE) + if (szb_page / SZB_CHAR > flen) + call fseti (IO_FD(io), F_FILESIZE, szb_page / SZB_CHAR) + + call sfree (sp) +end diff --git a/sys/qpoe/qpiowb.x b/sys/qpoe/qpiowb.x new file mode 100644 index 00000000..42f6ccc2 --- /dev/null +++ b/sys/qpoe/qpiowb.x @@ -0,0 +1,131 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <fset.h> +include "qpoe.h" +include "qpio.h" + +# QPIO_WBUCKET -- Flush any data currently in the bucket to the datafile, +# and set up the buffer to receive data for the bucket BKNO. The min/max +# event structs are updated whenever a bucket is written to disk. IO_EVI +# is assumed to point to the event following the last event written into +# in the buffer. Data should always be written sequentially. + +procedure qpio_wbucket (io, evi) + +pointer io #I QPIO descriptor +int evi #I evi of next bucket on exit + +pointer min_ev[2], max_ev[2], ev, fp, mp, dd +int sz_event, offset, dtype, nb, flen, nchars, i, j, k +int fstati() + +begin + dd = IO_DD(io) + + # Write the current bucket to the datafile if nonempty. + if (dd != NULL && IO_EVI(io) > IO_BKFIRSTEV(io)) { + # Scan through the events in the bucket and update the min/max + # event structs for the event list ([1] below) and for the + # bucket ([2] below, stored at the end of the bucket). + # Use CHAR pointers to facilitate pointer conversions. + + min_ev[1] = (IO_MINEVL(io) - 1) * SZ_SHORT + 1 + min_ev[2] = (IO_MINEVB(io) - 1) * SZ_SHORT + 1 + max_ev[1] = (IO_MAXEVL(io) - 1) * SZ_SHORT + 1 + max_ev[2] = (IO_MAXEVB(io) - 1) * SZ_SHORT + 1 + sz_event = DD_STRUCTLEN(dd) * SZ_STRUCT + + do k = 1, 2 { + ev = (IO_BP(io) - 1) * SZ_SHORT + 1 + # If min/max of bucket or first bucket of event list... + if (k == 2 || IO_BKNO(io) == 1) { + call amovc (Memc[ev], Memc[min_ev[k]], sz_event) + call amovc (Memc[ev], Memc[max_ev[k]], sz_event) + } + + do j = 1, IO_EVI(io) - IO_BKFIRSTEV(io) { + do i = 1, DD_NFIELDS(dd) { + # Get the typed offset and datatype of the field. + offset = DD_FOFFSET(dd,i) + dtype = DD_FTYPE(dd,i) + + # Update the min/max entries for the field. + switch (dtype) { + case TY_SHORT: + fp = (ev - 1) / SZ_SHORT + 1 + offset + mp = (min_ev[k] - 1) / SZ_SHORT + 1 + offset + if (Mems[fp] < Mems[mp]) + Mems[mp] = Mems[fp] + mp = (max_ev[k] - 1) / SZ_SHORT + 1 + offset + if (Mems[fp] > Mems[mp]) + Mems[mp] = Mems[fp] + + case TY_INT, TY_LONG: + fp = (ev - 1) / SZ_INT + 1 + offset + mp = (min_ev[k] - 1) / SZ_INT + 1 + offset + if (Memi[fp] < Memi[mp]) + Memi[mp] = Memi[fp] + mp = (max_ev[k] - 1) / SZ_INT + 1 + offset + if (Memi[fp] > Memi[mp]) + Memi[mp] = Memi[fp] + + case TY_REAL: + fp = (ev - 1) / SZ_REAL + 1 + offset + mp = (min_ev[k] - 1) / SZ_REAL + 1 + offset + if (Memr[fp] < Memr[mp]) + Memr[mp] = Memr[fp] + mp = (max_ev[k] - 1) / SZ_REAL + 1 + offset + if (Memr[fp] > Memr[mp]) + Memr[mp] = Memr[fp] + + case TY_DOUBLE: + fp = (ev - 1) / SZ_DOUBLE + 1 + offset + mp = (min_ev[k] - 1) / SZ_DOUBLE + 1 + offset + if (Memd[fp] < Memd[mp]) + Memd[mp] = Memd[fp] + mp = (max_ev[k] - 1) / SZ_DOUBLE + 1 + offset + if (Memd[fp] > Memd[mp]) + Memd[mp] = Memd[fp] + } + } + + ev = ev + sz_event + } + } + + # Zero out any remaining events. + while (ev < min_ev[2]) { + call aclrc (Memc[ev], sz_event) + ev = ev + sz_event + } + + # Write the bucket. + nb = IO_SZBBUCKET(io) + offset = (IO_BKNO(io) - 1) * nb + IO_FBOFF(io) + call fm_lfawrite (IO_CHAN(io), Mems[IO_BP(io)], nb, offset) + call fm_lfawait (IO_CHAN(io), nb) + + # Update the file size. + flen = fstati (IO_FD(io), F_FILESIZE) + nchars = (offset + nb) / SZB_CHAR + if (nchars > flen) + call fseti (IO_FD(io), F_FILESIZE, nchars) + + # Increment the total event count. + IO_NEVENTS(io) = max (IO_NEVENTS(io), IO_EVI(io) - 1) + S_NELEM(IO_PSYM(io)) = IO_NEVENTS(io) + QP_MODIFIED(IO_QP(io)) = YES + } + + # Set up the buffer for the new bucket. + IO_BKNO(io) = EVI_TO_BUCKET(io,evi) + IO_BKFIRSTEV(io) = BUCKET_TO_EVI(io,IO_BKNO(io)) + IO_BKLASTEV(io) = IO_BKFIRSTEV(io) + IO_BUCKETLEN(io) - 1 + + if (IO_DEBUG(io) > 2) { + call eprintf ("wbucket: evi=%d, bkno=%d\n") + call pargi(evi) + call pargi(IO_BKNO(io)) + } +end diff --git a/sys/qpoe/qplenf.x b/sys/qpoe/qplenf.x new file mode 100644 index 00000000..c8e4539e --- /dev/null +++ b/sys/qpoe/qplenf.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_LENF -- Return the length of the named parameter, i.e., the number of +# stored elements in the parameter value. NULL is returned if there is no +# value, or ERR if the parameter does not exist. + +int procedure qp_lenf (qp, param) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name + +pointer sym +pointer qp_gpsym() + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + sym = qp_gpsym (qp, param) + if (sym == NULL) + return (ERR) + else + return (S_NELEM(sym)) +end diff --git a/sys/qpoe/qploadwcs.x b/sys/qpoe/qploadwcs.x new file mode 100644 index 00000000..b4609ce7 --- /dev/null +++ b/sys/qpoe/qploadwcs.x @@ -0,0 +1,38 @@ +include <syserr.h> +include "qpoe.h" + +# QP_LOADWCS -- Load the default WCS, if there is one, from the QPOE image +# header. A QPOE file can contain any number of WCS, but the default WCS +# should relate the physical coordinate system, e.g., sky coordinates in +# the range 1024sq, 8192sq, etc., to world coordinates, e.g., the TAN +# projection. Probably we should provide for multiple physical coordinate +# systems (sky, detector, etc.) each with its own WCS, but at present we +# assume a single WCS. + +pointer procedure qp_loadwcs (qp) + +pointer qp #I QPOE descriptor + +int wcslen +pointer sp, svwcs, mw +errchk qp_lenf, syserrs, qp_read, mw_open +int qp_lenf(), qp_read() +pointer mw_open() +string s_qpwcs QPWCS + +begin + # Determine if there is a WCS, and if so, how big the saved version is. + wcslen = qp_lenf (qp, s_qpwcs) + if (wcslen <= 0) + call syserrs (SYS_QPNOWCS, QP_DFNAME(qp)) + + call smark (sp) + call salloc (svwcs, wcslen, TY_CHAR) + + # Retrieved the saved wcs, and load it into an MWCS descriptor. + wcslen = qp_read (qp, s_qpwcs, Memc[svwcs], wcslen, 1, "opaque") + mw = mw_open (svwcs, 0) + + call sfree (sp) + return (mw) +end diff --git a/sys/qpoe/qpmacro.x b/sys/qpoe/qpmacro.x new file mode 100644 index 00000000..984742d2 --- /dev/null +++ b/sys/qpoe/qpmacro.x @@ -0,0 +1,832 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <error.h> +include <finfo.h> +include <ctype.h> +include "qpoe.h" +include "qpex.h" + +.help qpmacro +.nf --------------------------------------------------------------------------- +QPMACRO -- Macro facility for QPOE. QPOE permits macro replacement in various +places, e.g., to alias parameter names, or enter predefined selection +expressions (selection functions). While macros may be defined permanently +in the datafile, they are more commonly defined by the user at runtime, and +used as a global facility to access any number of datafiles. Since we do not +want to store runtime macros in the datafile headers, the compiled definitions +cannot be entered into the datafile symbol table, but must be entered into a +separate global symbol table, maintained by QPOE and used to store runtime +macros to be used in all datafile accesses. + +The purpose of the package is to maintain an up to date global macro symbol +table. The symbol table itself is directly accessed by the client program, +rather than via the package interface, so that the standard SYMTAB package +routines may be used to access the symbol table. + + qm = qm_access () + st = qm_symtab (qm) + qm_setdefaults (qm, qp) + qm_upddefaults (qm, qp) + + qm_scan (qm, fname, flags) + qm_scano (qm, fd, flags) + +The macro symbol table is accessed with QM_ACCESS, which will compile or +update the in-core version of the symbol table if necessary. A call to +QM_SYMTAB is required to obtain the symbol table descriptor, a pointer, +which may change any time the symbol table is modified. QM_ACCESS should +be called only occasionally (e.g., at datafile open time) since it makes a +number of system calls to check file dates. QM_SYMTAB should be called +once upon entry to every routine which accesses the macro database. + +QM_SETDEFAULTS is called when a datafile is opened to set the default values +of all interface and datafile parameters; the user can control these defaults +by including SET statements in the macro definition file. QM_UPDDEFAULTS is +similar, except that it sets ONLY the values of those parameters that have +been explicitly set in SET statements in the macro files. + +When QM_ACCESS is called it looks for two variables in the user environment. + + QMSAVE The name of the file (default home$qpoe.msv) in + which the compiled macro database is to be saved, + or from which it is to be loaded. + + QMFILES A comma delimited list of macro definition (MD) + files to be scanned to compile the macro database. + (No default unless one is supplied by the local + system administrator). + +If the binary symbol table file QMSAVE exists and is newer than any of the MD +files then the symbol table is reloaded from the binary save file, else the MD +files are scanned and we attempt to write a new save file when done. If the +symbol table is already open and is newer than either the save file or the +MD files, then the routine exits immediately, returning a pointer to the global +QPOE macro database (symbol table). QM_SCAN and QM_SCANO are low level +routines for reading the contents of a MD file or stream into the symbol table. + +Note that at runtime, a completely different facility exists for macro +replacement; macros may be drawn from either source. The second mechanism +takes the name of the macro to be the *name* of a file in the current +directory containing the value string for the macro. This is less efficient, +but allows macros to be independently created and used dynamically at runtime. +The latter type of macros may be referenced only in QPOE selection expressions +(rather than as, for example, parameter name aliases). +.endhelp ---------------------------------------------------------------------- + +# Size limiting definitions. +define QM_MAXFILES 32 # maximum files in QMFILES list +define QM_SZCBUF 1024 # char storage for file list +define DEF_LENINDEX 50 # symbol table (init hash index) +define DEF_LENSTAB 256 # symbol table (init table len) +define DEF_SZSBUF 1024 # symbol table (init string buf len) +define SZ_MNAME 32 # max size macro name +define SZ_MVBUF 8192 # max size macro value + +# Defined parameters. +define QMFILES "qmfiles" # macro define file list +define QMSAVE "qmsave" # symtab save file +define DEF_QMSAVE "uparm$qpoe.msv" # default macro save file +define STTIME "$STTIME" # time of last st compile +define PSETKW "$PSETKW" # param used to store SET values +define QMSTNAME "QPOEMACROS" # symbol table name + +# Flags for QM_SCAN. +define QM_FLAGREDEFS 1B # complain about redefined macros + +# The QM descriptor (fixed pointer, while QM_ST is allowed to change). +define LEN_QM 1 +define QM_ST Memi[$1] # pointer to macro symbol table + +# The parameter set descriptor (for SET statements). +define LEN_PSET 32 # allow some extra space +define PS_EXPBLEN Memi[$1] # QPEX program buffer length +define PS_EXDBLEN Memi[$1+1] # QPEX data buffer length +define PS_EXMAXFRLLEN Memi[$1+2] # QPEX max FRLUT length +define PS_EXMAXRRLLEN Memi[$1+3] # QPEX max RRLUT length +define PS_EXLMINRANGES Memi[$1+4] # QPEX max ranges before using LUT +define PS_EXLSCALE Memi[$1+5] # QPEX scale nranges to LUT bins +define PS_SZPBBUF Memi[$1+6] # size of pushback buffer for macros +define PS_BUCKETLEN Memi[$1+7] # QPIO event file bucket size +define PS_FMMAXLFILES Memi[$1+8] # FMIO maxlfiles +define PS_FMMAXPTPAGES Memi[$1+9] # FMIO maxptpages (page table pages) +define PS_FMPAGESIZE Memi[$1+10] # FMIO pagesize +define PS_FMCACHESIZE Memi[$1+11] # FMIO buffer cache size +define PS_STINDEXLEN Memi[$1+12] # SYMTAB hash index length +define PS_STSTABLEN Memi[$1+13] # SYMTAB stab len (start) +define PS_STSBUFSIZE Memi[$1+14] # SYMTAB sbuf size (start) +define PS_NODEFFILT Memi[$1+15] # Disable use of default filter +define PS_NODEFMASK Memi[$1+16] # Disable use of default mask +define PS_XBLOCK Memr[P2R($1+17)]# QPIO blocking factor in X +define PS_YBLOCK Memr[P2R($1+18)]# QPIO blocking factor in Y +define PS_DEBUG Memi[$1+19] # debug level +define PS_OPTBUFSIZE Memi[$1+20] # QPIO/QPF FIO optimum buffer size + +# Handy macros. +define IS_PUNCT (IS_WHITE($1)||($1)==','||($1)=='\n') + + +# QM_ACCESS -- Access the QPOE macro descriptor. Once opened, this should +# remain open for the lifetime of the process. Since these macros are global, +# the single descriptor is shared by all open datafiles and all tasks in the +# process. + +pointer procedure qm_access() + +int nfiles, fd, i +bool save_file_exists +pointer file[QM_MAXFILES] +long fi[LEN_FINFO], date[QM_MAXFILES], stdate +pointer sp, qmfiles, qmsave, cbuf, qm, st, st_start, start, sym, ps, ip, op + +long clktime() +int envfind(), finfo(), open() +pointer stopen(), stenter(), stfind(), strestore() +errchk stopen, malloc, syserrs +string sttimekw STTIME +define uptodate_ 91 +data qm /NULL/ + +begin + call smark (sp) + call salloc (qmfiles, SZ_LINE, TY_CHAR) + call salloc (qmsave, SZ_PATHNAME, TY_CHAR) + call salloc (cbuf, QM_SZCBUF, TY_CHAR) + + # Open the QM descriptor only once (per process). + if (qm == NULL) { + # Allocate descriptor. + call malloc (qm, LEN_QM, TY_STRUCT) + + # Initialize symbol table. + st = stopen (QMSTNAME, DEF_LENINDEX, DEF_LENSTAB, DEF_SZSBUF) + sym = stenter (st, sttimekw, SZ_LONG); Meml[sym] = 0 + + # Initialize settable interface/datafile parameters. + ps = stenter (st, PSETKW, LEN_PSET) + call aclri (Memi[ps], LEN_PSET) + + # Free back to here when rebuilding symbol table. + call stmark (st, st_start) + } + + # Get the QMSAVE symtab save file filename. + if (envfind (QMSAVE, Memc[qmsave], SZ_PATHNAME) <= 0) + call strcpy (DEF_QMSAVE, Memc[qmsave], SZ_PATHNAME) + + # Get the QMFILES macro define file list. + if (envfind (QMFILES, Memc[qmfiles], SZ_LINE) <= 0) + Memc[qmfiles] = EOS + + # Process the QMFILES string into a list of filenames, and get the + # modify date of each file. + + nfiles = 0 + op = cbuf + ip = qmfiles + + repeat { + # Get the next comma delimited argument from QMFILES. + while (IS_PUNCT(Memc[ip])) + ip = ip + 1 + + start = op + while (Memc[ip] != EOS && !IS_PUNCT(Memc[ip])) { + Memc[op] = Memc[ip] + op = op + 1 + ip = ip + 1 + } + + Memc[op] = EOS + op = op + 1 + if (Memc[start] == EOS) + break + + # Add the file and its modify date to the file list. + if (finfo (Memc[start], fi) == ERR) { + call eprintf ("Warning: QPOE macro file %s not found\n") + call pargstr (Memc[start]) + } else { + nfiles = nfiles + 1 + if (nfiles > QM_MAXFILES) + call syserrs (SYS_QMNFILES, Memc[qmsave]) + file[nfiles] = start + date[nfiles] = fi[FI_MTIME] + } + } + + # Check the dates of the MD files against the in-core symbol table + # and exit if the symbol table is up to date. The date of the symbol + # table is stored in the table itself. + + sym = stfind (st, sttimekw) + if (nfiles > 0 && sym != NULL) { + stdate = Meml[sym] + for (i=1; i <= nfiles; i=i+1) + if (date[i] > stdate) + break + if (i > nfiles) + goto uptodate_ + } + + # If a binary symtab save file exists and is up to date, load it + # into the descriptor. + + save_file_exists = false + if (nfiles > 0) + save_file_exists = (finfo (Memc[qmsave], fi) == OK) + + if (save_file_exists) { + stdate = fi[FI_MTIME] + for (i=1; i <= nfiles; i=i+1) + if (date[i] > stdate) + break + if (i > nfiles || nfiles == 0) { + iferr (fd = open (Memc[qmsave], READ_ONLY, BINARY_FILE)) { + call eprintf ("Warning: cannot open ") + call eprintf ("QPOE macro save file %s\n") + call pargstr (Memc[qmsave]) + } else { + call stclose (st) + st = strestore (fd) + call close (fd) + goto uptodate_ + } + } + } + + # If we get here then we need to scan the MD files and build a new + # symbol table. + + # Rebuild the symbol table. + call stfree (st, st_start) + call stmark (st, st_start) + QM_ST(qm) = st + + for (i=1; i <= nfiles; i=i+1) + iferr (call qm_scan (qm, Memc[file[i]], 0)) + call erract (EA_WARN) + + # Set the time of last update. + Meml[sym] = clktime (0) + + # Update the save file if we have any defined macros. + if (nfiles > 0) { + call intr_disable() + if (save_file_exists) + iferr (call delete (Memc[qmsave])) + call erract (EA_WARN) + iferr (fd = open (Memc[qmsave], NEW_FILE, BINARY_FILE)) + call erract (EA_WARN) + else { + iferr (call stsave (st, fd)) + call erract (EA_WARN) + call close (fd) + } + call intr_enable() + } + +uptodate_ + call sfree (sp) + QM_ST(qm) = st + return (qm) +end + + +# QM_SYMTAB -- Get a pointer to the symbol table used to store the defined +# macros for QPOE. The level of indirection is needed so that the QM pointer +# can be fixed while the symtab pointer is allowed to change as the symbol +# table is modified or rebuilt. + +pointer procedure qm_symtab (qm) + +pointer qm #I QM descriptor + +begin + return (QM_ST(qm)) +end + + +# QM_SCAN -- Scan a macro definition (MD) file and add any macros defined +# therein into the symbol table. + +procedure qm_scan (qm, fname, flags) + +pointer qm #I QM descriptor +char fname[ARB] #I MD file name +int flags #I scan flags + +int fd +int open() +errchk open + +begin + fd = open (fname, READ_ONLY, TEXT_FILE) + call qm_scano (qm, fd, flags) + call close (fd) +end + + +# QM_SCANO -- Scan a stream and parse any macro defines therein, adding the +# defined macros to the given symbol table, and setting the values of the +# specified interface or datafile parameters. +# +# The syntax of a SET statement, used to set the default values of interface +# and datafile parameters, is as follows: +# +# set parameter value +# +# where the parameter names are as given in <qpset.h> (case not significant). +# Parameter values set in this way may be overridden by QP_SETI calls after +# opening a datafile. +# +# The syntax of a macro define is as follows: +# +# define name value +# +# where NAME is a simple alphanumeric identifier, and the string VALUE may +# contain references of the form $N, N=0:9, $0 being the macro name, $1:9 +# being replaced by the arguments to the macro when it is called. Newline +# may be escaped to enter multiline macro definition statements. Comments +# and blank lines are ignored. During macro expansion, any parenthesized +# arguments following the macro name will be consumed only if the macro as +# defined has symbolic arguments. The value string will be inserted without +# adding any whitespace at either end, and whitespace within the value string +# is significant. + +procedure qm_scano (qm, fd, flags) + +pointer qm #I QM descriptor +int fd #I input stream +int flags #I scan flags + +int ch +bool is_define, is_set +int symarg, junk, buflen, i +pointer sp, mname, mvbuf, sym, st, op, otop + +bool streq() +int qm_getc(), stpstr() +pointer stfind(), stenter() +errchk qm_getc, qm_setparam, stenter, stpstr, malloc, realloc +define next_ 91 + +begin + call smark (sp) + call salloc (mname, SZ_MNAME, TY_CHAR) + call malloc (mvbuf, SZ_MVBUF, TY_CHAR) + + st = QM_ST(qm) + junk = qm_getc (NULL, ch) + + # The following can only be set true in set statements, so we must + # initialize the values before processing the file. + + sym = stfind (st, PSETKW) + if (sym != NULL) { + PS_NODEFFILT(sym) = NO + PS_NODEFMASK(sym) = NO + } + + # Each loop processes one newline delimited statement from the + # input stream. The qm_getc function deals with continuation, + # blank lines and comments, etc. + + repeat { + # Get `define' and macro name (or `set' and parameter name). +next_ + do i = 1, 2 { + # Get identifier token. + op = mname + otop = mname + SZ_MNAME - 1 + while (qm_getc (fd, ch) != EOF) { + if (IS_ALNUM(ch) || ch == '_') { + Memc[op] = ch + op = min (otop, op + 1) + } else if (ch == '\n') { + if (op == mname) + goto next_ + else { + call ungetci (fd, ch) + break + } + } else if (IS_WHITE(ch) && op == mname) { + next + } else + break + } + Memc[op] = EOS + + # Process statement type keyword. + if (i == 1) { + is_define = (streq (Memc[mname], "define")) + is_set = (streq (Memc[mname], "set")) + + # Ignore statements other than SET or DEFINE. + if (!(is_define || is_set)) { + while (qm_getc (fd, ch) != EOF) + if (ch == '\n') + goto next_ + } + } + } + + # Check for EOF. + if (Memc[mname] == EOS) + break + + # Skip optional "=" if SET statement. + if (is_set) + while (IS_WHITE(ch)) { + if (qm_getc (fd, ch) == EOF) + break + else if (ch == '\n') + break + else if (ch == '=') + ch = ' ' + } + + # Skip to value string; leave first char in ch. + while (IS_WHITE(ch)) { + if (qm_getc (fd, ch) == EOF) + break + else if (ch == '\n') + break + } + + # Get value string. Check for the presence of any symbolic + # arguments of the form $N in the process. + + symarg = 0 + buflen = SZ_MVBUF + op = mvbuf + + Memc[op] = ch + op = op + 1 + + while (qm_getc (fd, ch) != EOF) { + if (ch == '\n') + break + else { + Memc[op] = ch + if (IS_DIGIT(ch)) + if (op > mvbuf) + if (Memc[op-1] == '$') + symarg = max (symarg, TO_INTEG(ch)) + op = op + 1 + if (op - mvbuf == buflen) { + call realloc (mvbuf, buflen + SZ_MVBUF, TY_CHAR) + op = mvbuf + buflen + buflen = buflen + SZ_MVBUF + } + } + } + Memc[op] = EOS + + # Process SET statements. + if (is_set) { + call strlwr (Memc[mname]) + call qm_setparam (qm, Memc[mname], Memc[mvbuf]) + next + } + + # Check for a redef. + if (and (flags, QM_FLAGREDEFS) != 0) { + sym = stfind (st, Memc[mname]) + if (sym != NULL) { + call eprintf ("Warning: QPOE macro `%s' redefined\n") + call pargstr (Memc[mname]) + } + } + + # Enter the macro into the symbol table. + sym = stenter (st, Memc[mname], LEN_SYMBOL) + S_OFFSET(sym) = stpstr (st, Memc[mvbuf], 0) + S_DTYPE(sym) = TY_MACRO + S_FLAGS(sym) = 0 + if (symarg > 0) + S_FLAGS(sym) = SF_MACARGS + else + S_FLAGS(sym) = 0 + } + + call mfree (mvbuf, TY_CHAR) + call sfree (sp) +end + + +# QM_SETPARAM -- Set the default value of an interface or datafile parameter. + +procedure qm_setparam (qm, param, valstr) + +pointer qm #I QM descriptor +char param[ARB] #I parameter to be set +char valstr[ARB] #I parameter value + +pointer ps +double dval +int value, ip, pp +int qp_ctoi(), qp_ctod(), strncmp() +pointer stfind() +bool streq() +errchk stfind +define err_ 91 + +begin + ps = stfind (QM_ST(qm), PSETKW) + if (ps == NULL) + return + + # Accept either QP_PARAM or just PARAM. + pp = 1 + if (strncmp (param, "qp_", 3) == 0) + pp = 4 + + # Decode the parameter value - mostly integer parameters at present, + # except for "nodeffilt" and "nodefmask" which do not have a value, + # and the blocking factors, which are floating point. + + ip = 1 + if (strncmp (param[pp], "nodef", 5) == 0) { + return + } else if (strncmp (param[pp], "block", 5) == 0) { + if (qp_ctod (valstr, ip, dval) <= 0) + goto err_ + PS_XBLOCK(ps) = dval + PS_YBLOCK(ps) = dval + return + } else if (strncmp (param[pp], "xblock", 6) == 0) { + if (qp_ctod (valstr, ip, dval) <= 0) + goto err_ + PS_XBLOCK(ps) = dval + return + } else if (strncmp (param[pp], "yblock", 6) == 0) { + if (qp_ctod (valstr, ip, dval) <= 0) + goto err_ + PS_YBLOCK(ps) = dval + return + } else { + if (qp_ctoi (valstr, ip, value) <= 0) { +err_ call eprintf ("bad value `%s' for QPOE parameter `%s'\n") + call pargstr (valstr) + call pargstr (param) + return + } + } + + # Set the parameter value in the global QM descriptor. + if ( streq (param[pp], "bucketlen")) + PS_BUCKETLEN(ps) = value + else if (streq (param[pp], "cachesize")) + PS_FMCACHESIZE(ps) = value + else if (streq (param[pp], "indexlen")) + PS_STINDEXLEN(ps) = value + else if (streq (param[pp], "maxlfiles")) + PS_FMMAXLFILES(ps) = value + else if (streq (param[pp], "maxptpages")) + PS_FMMAXPTPAGES(ps) = value + else if (streq (param[pp], "pagesize")) + PS_FMPAGESIZE(ps) = value + else if (streq (param[pp], "sbufsize")) + PS_STSBUFSIZE(ps) = value + else if (streq (param[pp], "stablen")) + PS_STSTABLEN(ps) = value + else if (streq (param[pp], "progbuflen")) + PS_EXPBLEN(ps) = value + else if (streq (param[pp], "databuflen")) + PS_EXDBLEN(ps) = value + else if (streq (param[pp], "maxfrlutlen")) + PS_EXMAXFRLLEN(ps) = value + else if (streq (param[pp], "maxrrlutlen")) + PS_EXMAXRRLLEN(ps) = value + else if (streq (param[pp], "lutminranges")) + PS_EXLMINRANGES(ps) = value + else if (streq (param[pp], "lutscale")) + PS_EXLSCALE(ps) = value + else if (streq (param[pp], "maxpushback")) + PS_SZPBBUF(ps) = value + else if (streq (param[pp], "nodeffilt")) + PS_NODEFFILT(ps) = YES + else if (streq (param[pp], "nodefmask")) + PS_NODEFMASK(ps) = YES + else if (streq (param[pp], "blockfactor")) + { PS_XBLOCK(ps) = value; PS_YBLOCK(ps) = value } + else if (streq (param[pp], "xblockfactor")) + PS_XBLOCK(ps) = value + else if (streq (param[pp], "yblockfactor")) + PS_YBLOCK(ps) = value + else if (streq (param[pp], "debuglevel")) + PS_DEBUG(ps) = value + else if (streq (param[pp], "optbufsize")) + PS_OPTBUFSIZE(ps) = value + else { + call eprintf ("unknown QPOE parameter `%s' in SET statement\n") + call pargstr (param) + } +end + + +# QM_SETDEFAULTS -- Set the current default values of all interface and +# datafile parameters in a QPOE descriptor. Called at datafile open time +# to get the defaults. + +procedure qm_setdefaults (qm, qp) + +pointer qm #I QM descriptor +pointer qp #I QPOE descriptor + +pointer ps +pointer stfind() +int qm_spari() +real qm_sparr() +errchk stfind + +begin + ps = stfind (QM_ST(qm), PSETKW) + if (ps == NULL) + return + + # Interface parameters. + QP_EXPBLEN(qp) = qm_spari (PS_EXPBLEN(ps), DEF_PROGBUFLEN) + QP_EXDBLEN(qp) = qm_spari (PS_EXDBLEN(ps), DEF_DATABUFLEN) + QP_EXMAXFRLLEN(qp) = qm_spari (PS_EXMAXFRLLEN(ps), DEF_MAXFRLUTLEN) + QP_EXMAXRRLLEN(qp) = qm_spari (PS_EXMAXRRLLEN(ps), DEF_MAXRRLUTLEN) + QP_EXLMINRANGES(qp) = qm_spari (PS_EXLMINRANGES(ps), DEF_LUTMINRANGES) + QP_EXLSCALE(qp) = qm_spari (PS_EXLSCALE(ps), DEF_LUTSCALE) + QP_SZPBBUF(qp) = qm_spari (PS_SZPBBUF(ps), DEF_MAXPUSHBACK) + QP_FMCACHESIZE(qp) = qm_spari (PS_FMCACHESIZE(ps), DEF_FMCACHESIZE) + + # Datafile parameters. + QP_BUCKETLEN(qp) = qm_spari (PS_BUCKETLEN(ps), DEF_BUCKETLEN) + QP_FMMAXLFILES(qp) = qm_spari (PS_FMMAXLFILES(ps), DEF_FMMAXLFILES) + QP_FMMAXPTPAGES(qp) = qm_spari (PS_FMMAXPTPAGES(ps), DEF_FMMAXPTPAGES) + QP_FMPAGESIZE(qp) = qm_spari (PS_FMPAGESIZE(ps), DEF_FMPAGESIZE) + QP_STINDEXLEN(qp) = qm_spari (PS_STINDEXLEN(ps), DEF_STINDEXLEN) + QP_STSTABLEN(qp) = qm_spari (PS_STSTABLEN(ps), DEF_STSTABLEN) + QP_STSBUFSIZE(qp) = qm_spari (PS_STSBUFSIZE(ps), DEF_STSBUFSIZE) + + # Other parameters. + QP_NODEFFILT(qp) = qm_spari (PS_NODEFFILT(ps), NO) + QP_NODEFMASK(qp) = qm_spari (PS_NODEFMASK(ps), NO) + QP_XBLOCK(qp) = qm_sparr (PS_XBLOCK(ps), DEF_BLOCKFACTOR) + QP_YBLOCK(qp) = qm_sparr (PS_YBLOCK(ps), DEF_BLOCKFACTOR) + QP_OPTBUFSIZE(qp) = qm_spari (PS_OPTBUFSIZE(ps), DEF_OPTBUFSIZE) + QP_DEBUG(qp) = qm_spari (PS_DEBUG(ps), 0) +end + + +# QM_SETPAR -- Return the given parameter value, if set in the user's macro +# files, otherwise return the interface default. + +int procedure qm_setpar (userval, defval) + +int userval #I user specified value, or zero +int defval #I interface default +int qm_spari() + +begin + return (qm_spari (userval, defval)) +end + + +# QM_SPARI -- Return the given int parameter value, if set in the user's macro +# files, otherwise return the interface default. + +int procedure qm_spari (userval, defval) + +int userval #I user specified value, or zero +int defval #I interface default + +begin + if (userval != 0) + return (userval) + else + return (defval) +end + + +# QM_SPARR -- Return the given real parameter value, if set in the user's macro +# files, otherwise return the interface default. + +real procedure qm_sparr (userval, defval) + +real userval #I user specified value, or zero +real defval #I interface default + +begin + if (userval != 0) + return (userval) + else + return (defval) +end + + +# QM_UPDDEFAULTS -- Update the values in the QPOE descriptor of all interface +# and datafile parameters set explicitly by a user macro or SET statement. +# Only those parameters for which values were explicitly specified in the +# use macro files are affected, allowing the use of global macros or set +# statements to override the interface or datafile defaults. + +procedure qm_upddefaults (qm, qp) + +pointer qm #I QM descriptor +pointer qp #I QPOE descriptor + +pointer ps +pointer stfind() +errchk stfind + +begin + ps = stfind (QM_ST(qm), PSETKW) + if (ps == NULL) + return + + # Interface parameters. + if (PS_EXPBLEN(ps) != 0) QP_EXPBLEN(qp) = PS_EXPBLEN(ps) + if (PS_EXDBLEN(ps) != 0) QP_EXDBLEN(qp) = PS_EXDBLEN(ps) + if (PS_EXMAXFRLLEN(ps) != 0) QP_EXMAXFRLLEN(qp) = PS_EXMAXFRLLEN(ps) + if (PS_EXMAXRRLLEN(ps) != 0) QP_EXMAXRRLLEN(qp) = PS_EXMAXRRLLEN(ps) + if (PS_EXLMINRANGES(ps) != 0) QP_EXLMINRANGES(qp)= PS_EXLMINRANGES(ps) + if (PS_EXLSCALE(ps) != 0) QP_EXLSCALE(qp) = PS_EXLSCALE(ps) + if (PS_SZPBBUF(ps) != 0) QP_SZPBBUF(qp) = PS_SZPBBUF(ps) + if (PS_FMCACHESIZE(ps) != 0) QP_FMCACHESIZE(qp) = PS_FMCACHESIZE(ps) + + # Datafile parameters. + if (PS_BUCKETLEN(ps) != 0) QP_BUCKETLEN(qp) = PS_BUCKETLEN(ps) + if (PS_FMMAXLFILES(ps) != 0) QP_FMMAXLFILES(qp) = PS_FMMAXLFILES(ps) + if (PS_FMMAXPTPAGES(ps) != 0) QP_FMMAXPTPAGES(qp)= PS_FMMAXPTPAGES(ps) + if (PS_FMPAGESIZE(ps) != 0) QP_FMPAGESIZE(qp) = PS_FMPAGESIZE(ps) + if (PS_STINDEXLEN(ps) != 0) QP_STINDEXLEN(qp) = PS_STINDEXLEN(ps) + if (PS_STSTABLEN(ps) != 0) QP_STSTABLEN(qp) = PS_STSTABLEN(ps) + if (PS_STSBUFSIZE(ps) != 0) QP_STSBUFSIZE(qp) = PS_STSBUFSIZE(ps) + + # Other parameters. + if (PS_NODEFFILT(ps) != 0) QP_NODEFFILT(qp) = PS_NODEFFILT(ps) + if (PS_NODEFMASK(ps) != 0) QP_NODEFMASK(qp) = PS_NODEFMASK(ps) + if (PS_XBLOCK(ps) != 0) QP_XBLOCK(qp) = PS_XBLOCK(ps) + if (PS_YBLOCK(ps) != 0) QP_YBLOCK(qp) = PS_YBLOCK(ps) + if (PS_OPTBUFSIZE(ps) != 0) QP_OPTBUFSIZE(qp) = PS_OPTBUFSIZE(ps) + if (PS_DEBUG(ps) != 0) QP_DEBUG(qp) = PS_DEBUG(ps) +end + + +# QM_GETC -- Return the next character from the input stream, ignoring +# comments and joining continued lines. The character value or EOF is +# returned as the function value. A call with FD=0 will initialize i/o +# for a new file. + +int procedure qm_getc (fd, ch) + +int fd #I input file +int ch #O returned character + +int quote +int getci() +errchk getci +define again_ 91 + +begin + # Initialization. + if (fd <= 0) { + quote = 0 + return (0) + } + + # Handle the most common cases first. +again_ + if (getci (fd, ch) == EOF) { + quote = 0 + return (EOF) + } else if (IS_ALNUM(ch)) + return (ch) + + # Handle the special cases - comments, escapes, quoted strings. + if (ch == '#' && quote == 0) { + # Skip a comment. + while (getci (fd, ch) != EOF) + if (ch == '\n') + goto again_ + } else if (ch == '\'' || ch == '"') { + # Toggle quoted string flag. + if (quote == 0) + quote = ch + else if (quote == ch) + quote = 0 + } else if (ch == '\\') { + # Process escapes. + if (getci (fd, ch) == '\n') + goto again_ + else if (quote == 0 && (ch == '\'' || ch == '"' || ch == '#')) + ; + else { + call ungetci (fd, ch) + ch = '\\' + } + } + + # Init context at end of every logical line. + if (ch == '\n') + quote = 0 + + return (ch) +end diff --git a/sys/qpoe/qpmkfname.x b/sys/qpoe/qpmkfname.x new file mode 100644 index 00000000..3721f6f4 --- /dev/null +++ b/sys/qpoe/qpmkfname.x @@ -0,0 +1,23 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_MKFNAME -- Construct the poefile filename, i.e., eliminate any whitespace +# and add the given extension if omitted. + +procedure qp_mkfname (poefile, extn, fname, maxch) + +char poefile[ARB] #I raw poefile name +char extn[ARB] #I extension to be added if absent +char fname[maxch] #O output filename +int maxch #I max chars out + +int n +bool strne() +int nowhite() + +begin + n = nowhite (poefile, fname, maxch) + if (n <= 3 || strne (fname[n-2], extn)) + call strcpy (extn, fname[n+1], maxch-n) +end diff --git a/sys/qpoe/qpoe.h b/sys/qpoe/qpoe.h new file mode 100644 index 00000000..41d07059 --- /dev/null +++ b/sys/qpoe/qpoe.h @@ -0,0 +1,115 @@ +# QPOE.H -- QPOE data definitions (private to the package). + +# Size limiting definitions. +define DEF_BLOCKFACTOR 1.0 # default block factor for image matrix +define DEF_BUCKETLEN 1024 # def nevents per bucket +define DEF_FMMAXLFILES 128 # def maxlfile per datafile +define DEF_FMMAXPTPAGES 256 # def maxptpages per datafile +define DEF_FMPAGESIZE 512 # def datafile page size +define DEF_FMCACHESIZE 8 # def buffer cache size +define DEF_STINDEXLEN 100 # def symtab hash index len +define DEF_STSTABLEN 2048 # initial symbol table len +define DEF_STSBUFSIZE 2048 # initial string buf size +define DEF_MAXPUSHBACK 8192 # max pushed back chars (macros) +define DEF_OPTBUFSIZE (512*512*2) # default buffer size for IMIO/QPF/FIO +define MAX_INDIR 20 # max macro indirection +define MAX_REDEF 20 # max entries for a symbol +define MAX_FIELDS 50 # max fields in a user structure +define INC_STRLEN 32 # unit of storage for strings +define LEN_PVAL 64 # max TY_USER struct size (in doubles) +define SZ_QPDFNAME 255 # max size QPOE filename +define SZ_TEXTBUF 2048 # handy text buffer for macro expansion +define SZ_TOKBUF 256 # token buffer size + +# Magic numbers. +define LF_QPOE 1 # QPOE file header and symbol table +define LF_STATICPARS 2 # static (fixed size) params +define QPOE_MAGIC 121120B # QPOE magic code (descriptor type) +define QPOE_VERSION 101 # QPOE interface version number +define QPOE_TITLE "QPOE-V1.2" # title string, for symbol table +define QPOE_EXTN ".qp" # QPOE file extension +define QPOE_MACROEXTN ".qpm" # QPOE macro definitions file extension +define QPWCS "qpwcs" # header parameter for default WCS +define IMMEDIATE 0 # for qp_sizeof +define INSTANCEOF 1 # for qp_sizeof + +# The main QPOE descriptor. +define LEN_QPDES 160 +define QP_MAGIC Memi[$1] # descriptor type code +define QP_VERSION Memi[$1+1] # QPOE version number +define QP_ACTIVE Memi[$1+2] # descriptor fully activated +define QP_FM Memi[$1+3] # datafile handle +define QP_ST Memi[$1+4] # datafile symbol table handle +define QP_QM Memi[$1+5] # global QPOE symbol table handle +define QP_MODE Memi[$1+6] # datafile access mode +define QP_OQP Memi[$1+7] # o_qp, if new copy file +define QP_EXPBLEN Memi[$1+8] # QPEX program buffer length +define QP_EXDBLEN Memi[$1+9] # QPEX data buffer length +define QP_EXMAXFRLLEN Memi[$1+10] # QPEX max FRLUT length +define QP_EXMAXRRLLEN Memi[$1+11] # QPEX max RRLUT length +define QP_EXLMINRANGES Memi[$1+12] # QPEX min ranges before using LUT +define QP_EXLSCALE Memi[$1+13] # QPEX scale nranges to LUT bins +define QP_SZPBBUF Memi[$1+14] # size of pushback buffer for macros +define QP_BUCKETLEN Memi[$1+15] # QPIO event file bucket size +define QP_FMMAXLFILES Memi[$1+16] # FMIO maxlfiles +define QP_FMMAXPTPAGES Memi[$1+17] # FMIO maxptpages +define QP_FMPAGESIZE Memi[$1+18] # FMIO pagesize +define QP_FMCACHESIZE Memi[$1+19] # FMIO buffer cache size +define QP_STINDEXLEN Memi[$1+20] # SYMTAB hash index length +define QP_STSTABLEN Memi[$1+21] # SYMTAB stab len (start) +define QP_STSBUFSIZE Memi[$1+22] # SYMTAB sbuf size (start) +define QP_STOFFSET Memi[$1+23] # lfile offset of stored symbol table +define QP_MODIFIED Memi[$1+24] # QPOE descriptor has been modified +define QP_DEBUG Memi[$1+25] # global debug level (debug messages) +define QP_XBLOCK Memr[P2R($1+26)]# default X blocking factor for QPIO +define QP_YBLOCK Memr[P2R($1+27)]# default Y blocking factor for QPIO +define QP_OPTBUFSIZE Memi[$1+28] # optimum buffer size for IMIO/QPF/FIO +define QP_NODEFFILT Memi[$1+29] # disable use of default filter +define QP_NODEFMASK Memi[$1+30] # disable use of default mask +define QP_DFNAME Memc[P2C($1+31)] # QPOE filename (for messages) + +# Symbol descriptor. +define LEN_SYMBOL 9 +define S_FLAGS Memi[$1] # integer flag word +define S_DTYPE Memi[$1+1] # datatype code +define S_DSYM Memi[$1+2] # offset of domain symbol if TY_USER +define S_NELEM Memi[$1+3] # number of elements of dtype +define S_MAXELEM Memi[$1+4] # allocated length +define S_SZELEM Memi[$1+5] # elsize, chars (primary domains only) +define S_COMMENT Memi[$1+6] # pointer to comment string in sbuf +define S_LFILE Memi[$1+7] # lfile where value is stored +define S_OFFSET Memi[$1+8] # char offset of value in lfile + +# Symbol flags. +define SF_DELETED 0001B # symbol has been deleted +define SF_INHERIT 0002B # inherit in NEW_COPY mode +define SF_MACARGS 0004B # macro symbol has symbolic arguments + +# QPOE special datatypes. +define SPPTYPES "bcsilrdx" # index is SPP TY_xxx type code +define TY_MACRO 15 # datafile local macro define +define TY_OPAQUE 16 # opaque (typeless) binary type +define TY_USER 17 # some user defined type + +# Lexical tokens. +define TOK_IDENTIFIER (-99) +define TOK_NUMBER (-98) +define TOK_STRING (-97) +define TOK_COMMAND (-96) +define TOK_PLUSEQUALS (-95) +define TOK_COLONEQUALS (-94) + +# QPOE header as stored in datafile. +define LEN_QPH 32 +define QPH_MAGIC Memi[$1] +define QPH_VERSION Memi[$1+1] +define QPH_STOFFSET Memi[$1+2] + +# Domain descriptor structure. +define LEN_DDDES 110 +define DD_STRUCTLEN Memi[$1] # structure length, su +define DD_NFIELDS Memi[$1+1] # number of fields in user structure +define DD_XFIELD Memi[$1+2] # field assigned to coordinate "x" +define DD_YFIELD Memi[$1+3] # field assigned to coordinate "y" +define DD_FOFFSET Memi[$1+10+$2-1]# array of field offsets +define DD_FTYPE Memi[$1+60+$2-1]# array of field datatypes diff --git a/sys/qpoe/qpopen.x b/sys/qpoe/qpopen.x new file mode 100644 index 00000000..d2b7e0b4 --- /dev/null +++ b/sys/qpoe/qpopen.x @@ -0,0 +1,132 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <fmset.h> +include "qpoe.h" +include "qpio.h" + +# QP_OPEN -- Open or create a QPOE datafile. This routine must be called +# before the poefile can be accessed. In the case of a create, the file +# parameters are not fixed until the first i/o or header access occurs, +# allowing one to use QP_SET calls to modify the file parameters after the +# open. + +pointer procedure qp_open (poefile, mode, o_qp) + +char poefile[ARB] #I QPOE file to be opened +int mode #I file access mode +pointer o_qp #I reference file, if NEW_COPY + +int fmmode, fd, n +pointer sp, qph, qp, fname, fm + +real qp_getr() +pointer fm_open(), strestore(), qm_access() +int fm_fopen(), read(), fm_stati(), qp_accessf() +errchk fm_open, strestore, fm_fopen, seek, read +errchk calloc, syserrs, qm_access + +string s_defblock DEF_BLOCK +string s_defxblock DEF_XBLOCK +string s_defyblock DEF_YBLOCK + +begin + call smark (sp) + call salloc (qph, LEN_QPH, TY_STRUCT) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + + # Construct the filename (with extension .qp) of the poefile. + call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME) + + # Open or create the poefile via the file manager. + fmmode = mode + if (mode == NEW_COPY) + fmmode = NEW_FILE + fm = fm_open (Memc[fname], fmmode) + + # Allocate the QPOE descriptor. + call calloc (qp, LEN_QPDES, TY_STRUCT) + call strcpy (Memc[fname], QP_DFNAME(qp), SZ_QPDFNAME) + + # Access the global macro database, and set the default values of + # all interface and datafile parameters. + + QP_QM(qp) = qm_access() + call qm_setdefaults (QP_QM(qp), qp) + + QP_MODE(qp) = mode + QP_OQP(qp) = o_qp + QP_FM(qp) = fm + + if (mode == NEW_FILE || mode == NEW_COPY) { + # Initialize the descriptor for a new poefile. The file + # attributes are not fixed until the file is accessed, to + # allow time to change the defaults with qp_seti. + + QP_MAGIC(qp) = QPOE_MAGIC + QP_VERSION(qp) = QPOE_VERSION + + if (mode == NEW_COPY) { + # Inherit datafile defaults from parent file. + QP_BUCKETLEN(qp) = QP_BUCKETLEN(o_qp) + QP_FMMAXLFILES(qp) = QP_FMMAXLFILES(o_qp) + QP_FMMAXPTPAGES(qp) = QP_FMMAXPTPAGES(o_qp) + QP_FMPAGESIZE(qp) = QP_FMPAGESIZE(o_qp) + QP_FMCACHESIZE(qp) = QP_FMCACHESIZE(o_qp) + QP_STINDEXLEN(qp) = QP_STINDEXLEN(o_qp) + QP_STSTABLEN(qp) = QP_STSTABLEN(o_qp) + QP_STSBUFSIZE(qp) = QP_STSBUFSIZE(o_qp) + } + + QP_ACTIVE(qp) = NO + + } else { + # Open an existing poefile. The encoded QPOE header and + # symbol table are stored in a binary lfile in the datafile. + + fd = fm_fopen (fm, LF_QPOE, READ_ONLY, BINARY_FILE) + + # Read the QPOE file header. + n = LEN_QPH * SZ_STRUCT + call aclri (Memi[qph], LEN_QPH) + if (read (fd, Memi[qph], n) < n) + call syserrs (SYS_QPBADFILE, QP_DFNAME(qp)) + call miiupk32 (Memi[qph], Memi[qph], LEN_QPH, TY_STRUCT) + + QP_MAGIC(qp) = QPH_MAGIC(qph) + QP_VERSION(qp) = QPH_VERSION(qph) + QP_STOFFSET(qp) = QPH_STOFFSET(qph) + + if (QP_MAGIC(qp) != QPOE_MAGIC) + call syserrs (SYS_QPBADFILE, QP_DFNAME(qp)) + + # Read the stored symbol table. + call seek (fd, QP_STOFFSET(qp)) + QP_ST(qp) = strestore (fd) + + # Initialize any remaining QP descriptor parameters. + QP_FMPAGESIZE(qp) = fm_stati (fm, FM_PAGESIZE) + call fm_seti (fm, FM_FCACHESIZE, DEF_FMCACHESIZE) + QP_ACTIVE(qp) = YES + + # See if the default block factor is set in the datafile header. + if (qp_accessf (qp, s_defblock) == YES) { + QP_XBLOCK(qp) = qp_getr (qp, s_defblock) + QP_YBLOCK(qp) = QP_XBLOCK(qp) + } + if (qp_accessf (qp, s_defxblock) == YES) + QP_XBLOCK(qp) = qp_getr (qp, s_defxblock) + if (qp_accessf (qp, s_defyblock) == YES) + QP_YBLOCK(qp) = qp_getr (qp, s_defyblock) + + call close (fd) + } + + # Allow any interface parameters set explicitly in global macro SET + # statements to override the inherited or datafile values set above. + + call qm_upddefaults (QP_QM(qp), qp) + + call sfree (sp) + return (qp) +end diff --git a/sys/qpoe/qpparse.x b/sys/qpoe/qpparse.x new file mode 100644 index 00000000..2bbd5cea --- /dev/null +++ b/sys/qpoe/qpparse.x @@ -0,0 +1,70 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <ctype.h> + +# QP_PARSE -- Parse a QPOE/QPIO specification into the root (poefile) name +# and event list filter expression fields. +# +# Syntax: root[filter] +# +# where the filter spec is optional. + +procedure qp_parse (qpspec, root, sz_root, filter, sz_filter) + +char qpspec[ARB] #I full event list specification +char root[sz_root] #O receives root name +int sz_root #I max chars in root name +char filter[sz_filter] #O receives filter +int sz_filter #I max chars in filter name + +int level, ip, op, ch + +begin + ip = 1 + op = 1 + + # Extract root name. The first (unescaped) [ marks the start of + # the filter field. + + for (ch=qpspec[ip]; ch != EOS && ch != '['; ch=qpspec[ip]) { + if (ch == '\\' && qpspec[ip+1] == '[') { + root[op] = '\\' + op = op + 1 + root[op] = '[' + ip = ip + 1 + } else + root[op] = ch + + op = min (sz_root, op + 1) + ip = ip + 1 + } + + root[op] = EOS + level = 0 + op = 1 + + # Extract the [] bracketed filter expression, allowing for nested + # brackets. + + for (ch=qpspec[ip]; ch != EOS; ch=qpspec[ip]) { + if (ch == '[') + level = level + 1 + else if (ch == ']') + level = level - 1 + + filter[op] = ch + op = min (sz_filter, op + 1) + + ip = ip + 1 + if (level <= 0) + break + } + + # Add closing brace if the user left it off. + if (op > 1 && ch != ']') { + filter[op] = ']' + op = min (sz_filter, op + 1) + } + + filter[op] = EOS +end diff --git a/sys/qpoe/qpparsefl.x b/sys/qpoe/qpparsefl.x new file mode 100644 index 00000000..566274fb --- /dev/null +++ b/sys/qpoe/qpparsefl.x @@ -0,0 +1,149 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" +include "qpex.h" + +# QP_PARSEFL -- Parse the field list, or declarations string for a user +# defined datatype (structure or domain). +# +# Syntax: { type1, type2, ..., typeN } +# +# e.g., {d,s:x,s:y,s,s,s,s} (Rosat/PROS event structure) +# +# where the TYPEi are primitive types, e.g., "r" or "real", "i" or "int", +# etc. Selected fields may have ":x" or ":y" appended to indicate that these +# are the default coordinate fields to be used for position based extraction. +# Fields will be automatically aligned as necessary, and the computed structure +# size will be forced to be an integral multiple of the largest datatype +# within the structure, to ensure proper alignment in arrays of the structures. + +int procedure qp_parsefl (qp, fieldlist, dd) + +pointer qp #I QPOE descriptor +char fieldlist[ARB] #I field list defining new datatype (domain) +pointer dd #U pointer to domain descriptor + +pointer sp, tokbuf, dsym, in +int nfields, offset, maxsize, xfield, yfield, token, dtype, fsize + +pointer qp_opentext() +int qp_gettok(), qp_nexttok(), sizeof(), qp_dtype() +errchk qp_gettok, qp_opentext, qp_nexttok +string qperr "QPOE structdef" +define nextfield_ 91 + +begin + call smark (sp) + call salloc (tokbuf, SZ_TOKBUF, TY_CHAR) + + # Open declarations string for non macro expanded token input. + in = qp_opentext (NULL, fieldlist) + + # Advance to structure terms list. + while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF) + if (Memc[tokbuf] == '{') + break + + nfields = 0 + offset = 0 + maxsize = 0 + xfield = 0 + yfield = 0 + + # Process the structure terms list. + repeat { + token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) + + switch (token) { # { + case EOF, '}': + break + + case TOK_IDENTIFIER: + # Get field datatype and size. + dtype = qp_dtype (qp, Memc[tokbuf], dsym) + if (dtype < TY_BOOL || dtype > TY_COMPLEX) { + call eprintf ("%s: bad field type `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + goto nextfield_ + } else + fsize = sizeof (dtype) + + # Output field descriptor. + nfields = nfields + 1 + if (nfields > MAX_FIELDS) { + call eprintf ("%s: too many fields `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + break + } + DD_FOFFSET(dd,nfields) = (offset + fsize-1) / fsize + DD_FTYPE(dd,nfields) = dtype + + # Update structure size parameters. + offset = (DD_FOFFSET(dd,nfields) * fsize) + fsize + maxsize = max (maxsize, fsize) + + # Process any :[XY] field modifiers. + if (qp_nexttok(in) == ':') { + repeat { + token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) + switch (Memc[tokbuf]) { + case ':': + next + case 'x': + if (xfield != 0) { + call eprintf ("%s: duplicate X field `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + } + xfield = nfields + break + case 'y': + if (yfield != 0) { + call eprintf ("%s: duplicate Y field `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + } + yfield = nfields + break + default: + call eprintf ("%s: unknown : field modifier `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + } + } + goto nextfield_ + } + case ',': + next + default: + call eprintf ("%s: unexpected token `%s'\n") + call pargstr (qperr) + call pargstr (Memc[tokbuf]) + } + +nextfield_ + # Read and discard tokens until we get to the next field. + while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF) + if (Memc[tokbuf] == ',') + break + } + + # Complete the domain descriptor initialization. + DD_NFIELDS(dd) = nfields + DD_XFIELD(dd) = xfield + DD_YFIELD(dd) = yfield + + # Pad the struct size to an integral multiple of the max field size. + if (nfields > 0) { + maxsize = max (SZ_STRUCT, maxsize) + DD_STRUCTLEN(dd) = (offset+maxsize-1)/maxsize*maxsize / SZ_STRUCT + } else + DD_STRUCTLEN(dd) = 0 + + call qp_closetext (in) + call sfree (sp) + + return (nfields) +end diff --git a/sys/qpoe/qppclose.x b/sys/qpoe/qppclose.x new file mode 100644 index 00000000..7a0bc9ab --- /dev/null +++ b/sys/qpoe/qppclose.x @@ -0,0 +1,27 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <fset.h> + +# QP_PCLOSE -- Close a parameter opened as a file with QP_POPEN. This +# differs from a simple call to fio.close in that the lfile used to store +# the parameter data is unlocked, as well as closing the file under FIO. + +procedure qp_pclose (fd) + +int fd #I file descriptor of QP_POPEN-ed parameter + +int lfile, type +pointer sp, lfname, fm +int fm_lfparse() + +begin + call smark (sp) + call salloc (lfname, SZ_FNAME, TY_CHAR) + + call fstats (fd, F_FILENAME, Memc[lfname], SZ_FNAME) + if (fm_lfparse (Memc[lfname], fm, lfile, type) != ERR) + call fm_unlock (fm, lfile) + + call close (fd) + call sfree (sp) +end diff --git a/sys/qpoe/qppopen.x b/sys/qpoe/qppopen.x new file mode 100644 index 00000000..4b0e2191 --- /dev/null +++ b/sys/qpoe/qppopen.x @@ -0,0 +1,62 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_POPEN -- Open a variable-array type parameter as a file. A call to +# fio.close is used to close the file. Note that the varlen parameter, which +# is stored in its own lfile in the datafile, is opened directly as a file +# independently of the FMIO file buffer cache. Most QPOE parameter i/o is +# via the cache, hence mixing QP_POPEN calls with ordinary QPOE i/o on the +# same parameter at the same time could lead to loss of data integrity due +# to the same lfile being opened simultaneously on two different file +# descriptors. We ensure that the lfile is not in the file cache at qp_popen +# time, but no checks are made once the file has been opened. A FIO file +# descriptor is returned as the function value; CLOSE should be called to +# close the file descriptor when it is no longer needed. + +int procedure qp_popen (qp, param, mode, type) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +int mode #I file(param) access mode +int type #I file type, text or binary + +pointer sym +int fm_fopen() +pointer qp_gpsym() +errchk qp_gpsym, qp_addf(), fm_lockout, syserrs + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + # Lookup the parameter; make sure it is a varlen parameter. + # Create a new parameter if none exists and the mode is NEW_FILE. + + sym = qp_gpsym (qp, param) + if (sym == NULL) { + if (mode != NEW_FILE) + call syserrs (SYS_QPUKNPAR, param) + else { + # Create a new parameter. + if (type == TEXT_FILE) + call qp_addf (qp, param, "c", 0, "", 0) + else + call qp_addf (qp, param, "opaque", 0, "", 0) + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + } + } else if (S_MAXELEM(sym) != 0) + call syserrs (SYS_QPPOPEN, param) + + # Place a lock on the file and then remove it, to cause an error + # if the lfile is already active in the file cache. + + call fm_lockout (QP_FM(qp), S_LFILE(sym)) + call fm_unlock (QP_FM(qp), S_LFILE(sym)) + + # Open the assigned lfile and return the file descriptor. + return (fm_fopen (QP_FM(qp), S_LFILE(sym), mode, type)) +end diff --git a/sys/qpoe/qpppar.x b/sys/qpoe/qpppar.x new file mode 100644 index 00000000..17d9496d --- /dev/null +++ b/sys/qpoe/qpppar.x @@ -0,0 +1,136 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <ctype.h> +include "qpoe.h" + +# QP_PUTPARAM -- Lookup the named parameter in the symbol table and return +# a pointer to a buffer into which the scalar parameter value is to be +# placed. A subsequent call to QPOE_FLUSHPAR updates the parameter value +# in the datafile. A NULL pointer is returned if the parameter exists but +# does not currently have a value. The parameter datatype code is returned +# as the function value. + +int procedure qp_putparam (qp, param, o_pp) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +pointer o_pp #O pointer to parameter value + +bool first_time +pointer sp, key, fm, op +int loc_pval, loc_Mem, ip, ch, sz_elem +data first_time /true/ + +int elem +pointer pp, sym +bool put_value +double pval[LEN_PVAL+1] +common /qppval/ pval, sym, elem, pp, put_value + +pointer qp_gpsym() +int ctoi(), qp_sizeof() +errchk qp_bind, syserrs + +begin + call smark (sp) + call salloc (key, SZ_FNAME, TY_CHAR) + + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + fm = QP_FM(qp) + + # Compute pointer (Memc index) to the static pval buffer. + # Make sure that the computed pointer is double aligned. + + if (first_time) { + call zlocva (pval, loc_pval) + call zlocva (Memc, loc_Mem) + pp = (loc_pval+SZ_DOUBLE - loc_Mem) / SZ_DOUBLE * SZ_DOUBLE + 1 + put_value = false + first_time = false + } else if (put_value) + call qp_flushpar (qp) + + # Extract the primary parameter name, minus any whitespace and + # subscript (e.g., "param[elem]"). + + op = key + do ip = 1, SZ_FNAME { + ch = param[ip] + if (IS_WHITE(ch)) + next + else if (ch == '[') + break + Memc[op] = ch + op = op + 1 + } + Memc[op] = EOS + + # Determine the array element (default [1]). + elem = 1 + if (param[ip] == '[') { + ip = ip + 1 + if (ctoi (param, ip, elem) <= 0) + elem = 1 + } + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, Memc[key]) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + + # Check to make sure storage for the parameter value exists, and + # set the parameter buffer pointer for the indicated datatype. + + sz_elem = qp_sizeof (qp, S_DTYPE(sym), sym, INSTANCEOF) + if (sz_elem > LEN_PVAL * SZ_DOUBLE) + call syserrs (SYS_QPPVALOVF, QP_DFNAME(qp)) + + if (elem < 1 || elem > S_MAXELEM(sym)) + o_pp = NULL + else if (S_DTYPE(sym) == TY_USER) + o_pp = (pp - 1) / SZ_STRUCT + 1 + else + o_pp = (pp - 1) / sz_elem + 1 + + # Set a flag to flush the value after the user has entered it. + put_value = true + + call sfree (sp) + return (S_DTYPE(sym)) +end + + +# QP_FLUSHPAR -- Update the saved parameter value in the indicated lfile. +# Repeated calls are harmless. + +procedure qp_flushpar (qp) + +pointer qp #I QPOE descriptor + +int sz_elem, fd +int qp_sizeof(), fm_getfd() +errchk fm_getfd, seek, write + +int elem +pointer pp, sym +bool put_value +double pval[LEN_PVAL+1] +common /qppval/ pval, sym, elem, pp, put_value + +begin + if (put_value) { + sz_elem = qp_sizeof (qp, S_DTYPE(sym), S_DSYM(sym), INSTANCEOF) + fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_WRITE, 0) + + call seek (fd, S_OFFSET(sym) + (elem - 1) * sz_elem) + call write (fd, Memc[pp], sz_elem) + S_NELEM(sym) = max (S_NELEM(sym), elem) + QP_MODIFIED(qp) = YES + + call fm_retfd (QP_FM(qp), S_LFILE(sym)) + put_value = false + } +end diff --git a/sys/qpoe/qppstr.x b/sys/qpoe/qppstr.x new file mode 100644 index 00000000..9ab2b402 --- /dev/null +++ b/sys/qpoe/qppstr.x @@ -0,0 +1,47 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_PSTR -- Update the string value of the named parameter. + +procedure qp_pstr (qp, param, strval) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char strval[ARB] #I new string value + +pointer fm, sym +int fd, nchars + +pointer qp_gpsym() +int fm_getfd(), strlen() +errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + fm = QP_FM(qp) + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + else if (S_DTYPE(sym) != TY_CHAR) + call syserrs (SYS_QPBADCONV, param) + + # Update the value of the parameter in the datafile. + fd = fm_getfd (fm, S_LFILE(sym), READ_WRITE, 0) + call seek (fd, S_OFFSET(sym)) + + nchars = strlen (strval) + if (S_MAXELEM(sym) > 0) + nchars = min (S_MAXELEM(sym), nchars) + + call write (fd, strval, nchars) + S_NELEM(sym) = nchars + QP_MODIFIED(qp) = YES + + call fm_retfd (fm, S_LFILE(sym)) +end diff --git a/sys/qpoe/qpput.gx b/sys/qpoe/qpput.gx new file mode 100644 index 00000000..871b2943 --- /dev/null +++ b/sys/qpoe/qpput.gx @@ -0,0 +1,74 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "../qpoe.h" + +# QP_PUT -- Set the value of the named header parameter. Automatic type +# conversion is performed where possible. While only scalar values can be +# set by this function, the scalar may be an element of a one-dimensional +# array, e.g., "param[N]". + +procedure qp_put$t (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +PIXEL value #I scalar parameter value + +pointer pp +bool indef +int dtype +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + dtype = qp_putparam (qp, param, pp) + if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + if (QP_DEBUG(qp) > 1) { + call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n") + call pargstr (param) + call pargi (TY_PIXEL) + call pargi (dtype) + call parg$t (value) + } + + indef = IS_INDEF(value) + + # Set the parameter value. + switch (dtype) { + case TY_CHAR: + Memc[pp] = value + case TY_SHORT: + if (indef) + Mems[pp] = INDEFS + else + Mems[pp] = value + case TY_INT: + if (indef) + Memi[pp] = INDEFI + else + Memi[pp] = value + case TY_LONG: + if (indef) + Meml[pp] = INDEFL + else + Meml[pp] = value + case TY_REAL: + if (indef) + Memr[pp] = INDEFR + else + Memr[pp] = value + case TY_DOUBLE: + if (indef) + Memd[pp] = INDEFD + else + Memd[pp] = value + default: + call syserrs (SYS_QPBADCONV, param) + } + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/qpputb.x b/sys/qpoe/qpputb.x new file mode 100644 index 00000000..da7f7ba8 --- /dev/null +++ b/sys/qpoe/qpputb.x @@ -0,0 +1,31 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_PUTB -- Set the boolean value of the named header parameter. Type +# conversion is not permitted between boolean and the other data types. + +procedure qp_putb (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +bool value #I scalar parameter value + +pointer pp +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + if (qp_putparam (qp, param, pp) != TY_BOOL) + call syserrs (SYS_QPBADCONV, param) + else if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Pass the new value. + Memb[pp] = value + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/qpputx.x b/sys/qpoe/qpputx.x new file mode 100644 index 00000000..0ed73b35 --- /dev/null +++ b/sys/qpoe/qpputx.x @@ -0,0 +1,31 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_PUTX -- Set the complex value of the named header parameter. Type +# conversion is not permitted between complex and the other data types. + +procedure qp_putx (qp, param, value) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +complex value #I scalar parameter value + +pointer pp +int qp_putparam() +errchk qp_putparam, syserrs + +begin + # Lookup the parameter and get a pointer to the value buffer. + if (qp_putparam (qp, param, pp) != TY_COMPLEX) + call syserrs (SYS_QPBADCONV, param) + else if (pp == NULL) + call syserrs (SYS_QPNOVAL, param) + + # Pass the new value. + Memx[pp] = value + + # Update the parameter in the datafile. + call qp_flushpar (qp) +end diff --git a/sys/qpoe/qpqueryf.x b/sys/qpoe/qpqueryf.x new file mode 100644 index 00000000..c8de988a --- /dev/null +++ b/sys/qpoe/qpqueryf.x @@ -0,0 +1,91 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include <qpset.h> +include "qpoe.h" + +# QP_QUERYF -- Get information describing the named parameter. The current +# vector length of the parameter is returned as the function value, or ERR +# if the parameter does not exist. + +int procedure qp_queryf (qp, param, datatype, maxelem, comment, flags) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char datatype[SZ_DATATYPE] #O parameter data type +int maxelem #O allocated length of parameter +char comment[SZ_COMMENT] #O comment describing parameter +int flags #O parameter flag word + +int junk +pointer sym, dsym, ip, st + +int qp_gstr() +pointer qp_gpsym(), stname(), strefstab(), strefsbuf() +errchk qp_gpsym, stname, syserrs + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + st = QP_ST(qp) + + # Locate the symbol. + sym = qp_gpsym (qp, param) + if (sym == NULL) + return (ERR) + + flags = S_FLAGS(sym) + maxelem = S_MAXELEM(sym) + + # Output the symbolic datatype. + datatype[2] = EOS + switch (S_DTYPE(sym)) { + case TY_BOOL: + datatype[1] = 'b' + case TY_CHAR: + datatype[1] = 'c' + case TY_SHORT: + datatype[1] = 's' + case TY_INT: + datatype[1] = 'i' + case TY_LONG: + datatype[1] = 'l' + case TY_REAL: + datatype[1] = 'r' + case TY_DOUBLE: + datatype[1] = 'd' + case TY_COMPLEX: + datatype[1] = 'x' + + case TY_MACRO: + call strcpy ("macro", datatype, SZ_DATATYPE) + case TY_OPAQUE: + call strcpy ("opaque", datatype, SZ_DATATYPE) + + case TY_USER: + # User defined type: if S_DSYM is NULL, this is the domain entry + # itself, else the domain name is the datatype of the parameter. + # If this is a primary domain entry, the field list defining the + # structure is stored as the string value of the parameter. + + if (S_DSYM(sym) == NULL) + junk = qp_gstr (qp, param, datatype, SZ_DATATYPE) + else { + dsym = strefstab (st, S_DSYM(sym)) + call strcpy (Memc[stname(st,dsym)], datatype, SZ_DATATYPE) + } + + default: + call strcpy ("<unknown>", datatype, SZ_DATATYPE) + } + + # Output the comment field. + if (S_COMMENT(sym) != NULL) { + ip = strefsbuf (st, S_COMMENT(sym)) + call strcpy (Memc[ip], comment, SZ_COMMENT) + } else + comment[1] = EOS + + return (S_NELEM(sym)) +end diff --git a/sys/qpoe/qpread.x b/sys/qpoe/qpread.x new file mode 100644 index 00000000..c25fe506 --- /dev/null +++ b/sys/qpoe/qpread.x @@ -0,0 +1,80 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_READ -- Read a range of elements from a parameter. Works for any +# parameter, including scalar parameters and both static and variable +# length array valued parameters. Automatic datatype conversion is +# performed for the primitive types. + +int procedure qp_read (qp, param, buf, maxelem, first, datatype) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char buf[ARB] #O user data buffer to receive data +int maxelem #I max number of data elements to read +int first #I first data element to read +char datatype[ARB] #I datatype to be returned + +pointer sp, fm, sym, tbuf, isym, osym +int fd, sz_itype, sz_otype, nelem, itype, otype + +pointer qp_gpsym() +int fm_getfd(), qp_sizeof(), read(), qp_dtype() +errchk qp_bind, qp_gpsym, fm_getfd, seek, read, syserrs + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + fm = QP_FM(qp) + otype = qp_dtype (qp, datatype, osym) + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + else { + itype = S_DTYPE(sym) + isym = sym + } + + # Determine the number of inbounds elements. + nelem = max(0, min(maxelem, S_NELEM(sym) - first + 1)) + if (first <= 0) + call syserrs (SYS_QPINDXOOR, param) + + # Verify that any type conversion is legal. + if (otype != itype) + if (min(otype,itype) < TY_CHAR || max(otype,itype) > TY_DOUBLE) + call syserrs (SYS_QPBADCONV, param) + + # Open the lfile and read the data segment. + fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0) + + if (nelem > 0) { + sz_itype = qp_sizeof (qp, itype, isym, IMMEDIATE) + sz_otype = qp_sizeof (qp, otype, osym, INSTANCEOF) + + # Read and output the data. + call seek (fd, S_OFFSET(sym) + (first - 1) * sz_itype) + if (sz_itype <= sz_otype) { + # Read the data directly into the user's buffer. + nelem = read (fd, buf, nelem * sz_itype) / sz_itype + if (nelem > 0 && otype != itype) + call acht (buf, buf, nelem, itype, otype) + } else { + # Read the data into a temporary buffer. + call smark (sp) + call salloc (tbuf, nelem * sz_itype, TY_CHAR) + nelem = read (fd, Memc[tbuf], nelem * sz_itype) / sz_itype + if (nelem > 0) + call acht (Memc[tbuf], buf, nelem, itype, otype) + call sfree (sp) + } + } + + call fm_retfd (fm, S_LFILE(sym)) + return (nelem) +end diff --git a/sys/qpoe/qprebuild.x b/sys/qpoe/qprebuild.x new file mode 100644 index 00000000..369eba1e --- /dev/null +++ b/sys/qpoe/qprebuild.x @@ -0,0 +1,21 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_REBUILD -- Rebuild a poefile to reclaim unused space, and render storage +# elements logically contiguous to improve file access efficiency. + +procedure qp_rebuild (poefile) + +char poefile[ARB] #I poefile name +pointer sp, fname + +begin + call smark (sp) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + + call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME) + call fm_rebuild (Memc[fname]) + + call sfree (sp) +end diff --git a/sys/qpoe/qprename.x b/sys/qpoe/qprename.x new file mode 100644 index 00000000..2287b0fd --- /dev/null +++ b/sys/qpoe/qprename.x @@ -0,0 +1,25 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_RENAME -- Rename a poefile. + +procedure qp_rename (o_poefile, n_poefile) + +char o_poefile[ARB] #I old poefile name +char n_poefile[ARB] #I new poefile name + +pointer sp, o_fname, n_fname +string extn QPOE_EXTN + +begin + call smark (sp) + call salloc (o_fname, SZ_PATHNAME, TY_CHAR) + call salloc (n_fname, SZ_PATHNAME, TY_CHAR) + + call qp_mkfname (o_poefile, extn, Memc[o_fname], SZ_PATHNAME) + call qp_mkfname (n_poefile, extn, Memc[n_fname], SZ_PATHNAME) + call fm_rename (Memc[o_fname], Memc[n_fname]) + + call sfree (sp) +end diff --git a/sys/qpoe/qprenamef.x b/sys/qpoe/qprenamef.x new file mode 100644 index 00000000..39abb915 --- /dev/null +++ b/sys/qpoe/qprenamef.x @@ -0,0 +1,48 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_RENAMEF -- Rename a header parameter. It is an error if the named header +# parameter does not exist, or if the new name would redefine another symbol. + +procedure qp_renamef (qp, param, newname) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char newname[ARB] #I new parameter name + +pointer sym, nsym, st +pointer qp_gpsym(), stenter() +errchk qp_gpsym, syserrs, stenter + +begin + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + st = QP_ST(qp) + + # Access the named parameter. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + + # Check for a parameter redefinition. + nsym = qp_gpsym (qp, newname) + if (nsym != NULL) + call syserrs (SYS_QPREDEF, newname) + + # Rename the symbol. We cannot just edit the name, as the hash + # function would not be able to find it. We must create a new + # symstruct and replace the old one by it. The stenter can cause + # reallocation of the symbol table, so we need to recompute the + # symbol pointer. + + nsym = stenter (st, newname, LEN_SYMBOL) + sym = qp_gpsym (qp, param) + + call amovi (Memi[sym], Memi[nsym], LEN_SYMBOL) + S_FLAGS(sym) = or (S_FLAGS(sym), SF_DELETED) + + QP_MODIFIED(qp) = YES +end diff --git a/sys/qpoe/qprlmerge.gx b/sys/qpoe/qprlmerge.gx new file mode 100644 index 00000000..410bb952 --- /dev/null +++ b/sys/qpoe/qprlmerge.gx @@ -0,0 +1,134 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include "../qpex.h" + +# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are +# common to both range lists are output. The number of ranges in the +# output range list is returned as the function value. + +int procedure qp_rlmerge$t (os,oe,olen, xs,xe,nx, ys,ye,ny) + +pointer os, oe #U output range list +int olen #U allocated length of OS, OE arrays + +PIXEL xs[ARB], xe[ARB] #I range list to be merged with +int nx #I number of ranges in X list +PIXEL ys[ARB], ye[ARB] #I range list to be merged with X +int ny #I number of ranges in Y list + +PIXEL o1, o2 +int nx_out, xi, yi, i +PIXEL qp_minval$t(), qp_maxval$t() +bool qp_lessthan$t() +errchk realloc + +begin + nx_out = 0 + if (nx <= 0 || ny <= 0) + return (0) + + xi = 1 + yi = 1 + + do i = 1, ARB { + # Find a pair of ranges which intersect. + repeat { + if (qp_lessthan$t (xe[xi], ys[yi])) { + if (xi >= nx) + return (nx_out) + else + xi = xi + 1 + } else if (qp_lessthan$t (ye[yi], xs[xi])) { + if (yi >= ny) + return (nx_out) + else + yi = yi + 1 + } else + break + } + + # Compute the intersection. + o1 = qp_maxval$t (xs[xi], ys[yi]) + o2 = qp_minval$t (xe[xi], ye[yi]) + + # Output the range. + if (nx_out + 1 > olen) { + olen = max (DEF_XLEN, olen * 2) + call realloc (os, olen, TY_PIXEL) + call realloc (oe, olen, TY_PIXEL) + } + + Mem$t[os+nx_out] = o1 + Mem$t[oe+nx_out] = o2 + nx_out = nx_out + 1 + + # Advance to the next range. + if (xi < nx && qp_lessthan$t (xe[xi], ye[yi])) + xi = xi + 1 + else if (yi < ny) + yi = yi + 1 + else + break + } + + return (nx_out) +end + + +# QP_MINVAL -- Return the lesser of two values, where either value can +# be an open range. + +PIXEL procedure qp_minval$t (x, y) + +PIXEL x #I first value +PIXEL y #I second value + +bool qp_lessthan$t() + +begin + if (qp_lessthan$t (x, y)) + return (x) + else + return (y) +end + + +# QP_MAXVAL -- Return the greater of two values, where either value can +# be an open range. + +PIXEL procedure qp_maxval$t (x, y) + +PIXEL x #I first value +PIXEL y #I second value + +bool qp_lessthan$t() + +begin + if (qp_lessthan$t (x, y)) + return (y) + else + return (x) +end + + +# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open +# range values. + +bool procedure qp_lessthan$t (x, y) + +PIXEL x #I first value +PIXEL y #I second value + +begin + if (IS_LEFT$T(x)) + return (!IS_LEFT$T(y)) + else if (IS_RIGHT$T(x)) + return (false) + else if (IS_LEFT$T(y)) + return (false) + else if (IS_RIGHT$T(y)) + return (true) + else + return (x < y) +end diff --git a/sys/qpoe/qpsavewcs.x b/sys/qpoe/qpsavewcs.x new file mode 100644 index 00000000..35293d62 --- /dev/null +++ b/sys/qpoe/qpsavewcs.x @@ -0,0 +1,35 @@ +include "qpoe.h" + +# QP_SAVEWCS -- Save the given WCS in the QPOE header, as a variable length +# binary array (machine independent) in the default WCS parameter QPWCS. + +procedure qp_savewcs (qp, mw) + +pointer qp #I QPOE descriptor +pointer mw #I MWCS descriptor + +pointer bp +int buflen, nchars +int mw_save(), qp_accessf() +errchk mw_save, qp_accessf, qp_addf, qp_write +string s_opaque "opaque" +string s_qpwcs QPWCS + +begin + bp = NULL + buflen = 0 + + # Encode the WCS as a machine independent binary array. + nchars = mw_save (mw, bp, buflen) + + # Save it in the QPOE header. + if (nchars > 0) { + if (qp_accessf (qp, s_qpwcs) == NO) + call qp_addf (qp, s_qpwcs, + s_opaque, 0, "World coordinate system", SF_INHERIT) + call qp_write (qp, s_qpwcs, Memc[bp], nchars, 1, s_opaque) + } + + if (bp != NULL) + call mfree (bp, TY_CHAR) +end diff --git a/sys/qpoe/qpseti.x b/sys/qpoe/qpseti.x new file mode 100644 index 00000000..3a18c35e --- /dev/null +++ b/sys/qpoe/qpseti.x @@ -0,0 +1,62 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpset.h> +include "qpoe.h" + +# QP_SETI -- Set an QPOE integer parameter. + +procedure qp_seti (qp, param, value) + +pointer qp #I QPOE descriptor +int param #I parameter to be set +int value #I new value for parameter + +begin + switch (param) { + case QPOE_BLOCKFACTOR: + QP_XBLOCK(qp) = value + QP_YBLOCK(qp) = value + case QPOE_XBLOCKFACTOR: + QP_XBLOCK(qp) = value + case QPOE_YBLOCKFACTOR: + QP_YBLOCK(qp) = value + case QPOE_BUCKETLEN: + QP_BUCKETLEN(qp) = value + case QPOE_CACHESIZE: + QP_FMCACHESIZE(qp) = value + case QPOE_DATABUFLEN: + QP_EXDBLEN(qp) = value + case QPOE_DEBUGLEVEL: + QP_DEBUG(qp) = value + case QPOE_INDEXLEN: + QP_STINDEXLEN(qp) = value + case QPOE_LUTMINRANGES: + QP_EXLMINRANGES(qp) = value + case QPOE_LUTSCALE: + QP_EXLSCALE(qp) = value + case QPOE_MAXFRLUTLEN: + QP_EXMAXFRLLEN(qp) = value + case QPOE_MAXLFILES: + QP_FMMAXLFILES(qp) = value + case QPOE_MAXPTPAGES: + QP_FMMAXPTPAGES(qp) = value + case QPOE_MAXRRLUTLEN: + QP_EXMAXRRLLEN(qp) = value + case QPOE_MAXPUSHBACK: + QP_SZPBBUF(qp) = value + case QPOE_NODEFFILT: + QP_NODEFFILT(qp) = value + case QPOE_NODEFMASK: + QP_NODEFMASK(qp) = value + case QPOE_OPTBUFSIZE: + QP_OPTBUFSIZE(qp) = value + case QPOE_PAGESIZE: + QP_FMPAGESIZE(qp) = value + case QPOE_PROGBUFLEN: + QP_EXPBLEN(qp) = value + case QPOE_SBUFSIZE: + QP_STSBUFSIZE(qp) = value + case QPOE_STABLEN: + QP_STSTABLEN(qp) = value + } +end diff --git a/sys/qpoe/qpsetr.x b/sys/qpoe/qpsetr.x new file mode 100644 index 00000000..9378912f --- /dev/null +++ b/sys/qpoe/qpsetr.x @@ -0,0 +1,24 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpset.h> +include "qpoe.h" + +# QP_SETR -- Set an QPOE real valued parameter. + +procedure qp_setr (qp, param, value) + +pointer qp #I QPOE descriptor +int param #I parameter to be set +real value #I new value for parameter + +begin + switch (param) { + case QPOE_BLOCKFACTOR: + QP_XBLOCK(qp) = value + QP_YBLOCK(qp) = value + case QPOE_XBLOCKFACTOR: + QP_XBLOCK(qp) = value + case QPOE_YBLOCKFACTOR: + QP_YBLOCK(qp) = value + } +end diff --git a/sys/qpoe/qpsizeof.x b/sys/qpoe/qpsizeof.x new file mode 100644 index 00000000..bfa1698f --- /dev/null +++ b/sys/qpoe/qpsizeof.x @@ -0,0 +1,46 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_SIZEOF -- Determine the size in chars of a QPOE datatype. This may +# be one of the special datatypes (user defined record types), or a primitive +# type. In the case of a special type, the REFTYPE flag specifies whether +# the size of the value of the type variable itself (always SZ_CHAR) is to be +# returned, or the size of an *instance* of the special type. + +int procedure qp_sizeof (qp, dtype, dsym, reftype) + +pointer qp #I QPOE descriptor +int dtype #I datatype code +pointer dsym #I domain descriptor, if type TY_USER +int reftype #I IMMEDIATE (domain itself) or INSTANCEOF + +pointer sym +int sizeof() +pointer strefstab() + +begin + switch (dtype) { + case TY_MACRO, TY_OPAQUE: + return (SZ_CHAR) + + case TY_USER: + # Size of a user defined structure (or the element size of the + # struct definition entry itself). + + if (dsym == NULL) { # {...} + return (SZ_CHAR) + } else if (S_DSYM(dsym) == NULL) { # reference is to + if (reftype == IMMEDIATE) # primary domain entry + return (SZ_CHAR) + else + return (S_SZELEM(dsym)) + } else { # instance of domain + sym = strefstab (QP_ST(qp), S_DSYM(dsym)) + return (S_SZELEM(sym)) + } + + default: + return (sizeof (dtype)) + } +end diff --git a/sys/qpoe/qpstati.x b/sys/qpoe/qpstati.x new file mode 100644 index 00000000..df8d176c --- /dev/null +++ b/sys/qpoe/qpstati.x @@ -0,0 +1,76 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpset.h> +include "qpoe.h" + +# QP_STATI -- Get the value of an QPOE integer parameter. + +int procedure qp_stati (qp, param) + +pointer qp #I QPOE descriptor +int param #I parameter to be queried + +bool fp_equalr() + +begin + switch (param) { + case QPOE_BLOCKFACTOR: # standard params + if (fp_equalr (QP_XBLOCK(qp), QP_YBLOCK(qp))) + return (QP_XBLOCK(qp)) + else + return (ERR) + case QPOE_XBLOCKFACTOR: + return (QP_XBLOCK(qp)) + case QPOE_YBLOCKFACTOR: + return (QP_YBLOCK(qp)) + case QPOE_BUCKETLEN: + return (QP_BUCKETLEN(qp)) + case QPOE_CACHESIZE: + return (QP_FMCACHESIZE(qp)) + case QPOE_DATABUFLEN: + return (QP_EXDBLEN(qp)) + case QPOE_DEBUGLEVEL: + return (QP_DEBUG(qp)) + case QPOE_INDEXLEN: + return (QP_STINDEXLEN(qp)) + case QPOE_MAXLFILES: + return (QP_FMMAXLFILES(qp)) + case QPOE_MAXPTPAGES: + return (QP_FMMAXPTPAGES(qp)) + case QPOE_MAXFRLUTLEN: + return (QP_EXMAXFRLLEN(qp)) + case QPOE_MAXRRLUTLEN: + return (QP_EXMAXRRLLEN(qp)) + case QPOE_LUTMINRANGES: + return (QP_EXLMINRANGES(qp)) + case QPOE_LUTSCALE: + return (QP_EXLSCALE(qp)) + case QPOE_MAXPUSHBACK: + return (QP_SZPBBUF(qp)) + case QPOE_NODEFFILT: + return (QP_NODEFFILT(qp)) + case QPOE_NODEFMASK: + return (QP_NODEFMASK(qp)) + case QPOE_OPTBUFSIZE: + return (QP_OPTBUFSIZE(qp)) + case QPOE_PAGESIZE: + return (QP_FMPAGESIZE(qp)) + case QPOE_PROGBUFLEN: + return (QP_EXPBLEN(qp)) + case QPOE_SBUFSIZE: + return (QP_STSBUFSIZE(qp)) + case QPOE_STABLEN: + return (QP_STSTABLEN(qp)) + + case QPOE_FM: # read-only params + return (QP_FM(qp)) + case QPOE_MODE: + return (QP_MODE(qp)) + case QPOE_ST: + return (QP_ST(qp)) + case QPOE_VERSION: + return (QP_VERSION(qp)) + } + + return (ERR) +end diff --git a/sys/qpoe/qpstatr.x b/sys/qpoe/qpstatr.x new file mode 100644 index 00000000..0b3aec99 --- /dev/null +++ b/sys/qpoe/qpstatr.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <qpset.h> +include "qpoe.h" + +# QP_STATR -- Get the value of an QPOE real parameter. + +real procedure qp_statr (qp, param) + +pointer qp #I QPOE descriptor +int param #I parameter to be queried + +bool fp_equalr() + +begin + switch (param) { + case QPOE_BLOCKFACTOR: # standard params + if (fp_equalr (QP_XBLOCK(qp), QP_YBLOCK(qp))) + return (QP_XBLOCK(qp)) + else + return (ERR) + case QPOE_XBLOCKFACTOR: + return (QP_XBLOCK(qp)) + case QPOE_YBLOCKFACTOR: + return (QP_YBLOCK(qp)) + } + + return (ERR) +end diff --git a/sys/qpoe/qpsync.x b/sys/qpoe/qpsync.x new file mode 100644 index 00000000..692136d6 --- /dev/null +++ b/sys/qpoe/qpsync.x @@ -0,0 +1,51 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "qpoe.h" + +# QP_SYNC -- Update the poefile on disk. + +procedure qp_sync (qp) + +pointer qp #I QPOE descriptor + +int n, fd +pointer sp, qph +int fm_fopen() +errchk qp_flushpar, fm_fopen, write, stsave + +begin + # Flush the put-parameter buffer. + call qp_flushpar (qp) + + # Update the QPOE descriptor and symbol table in the datafile. + if (QP_MODIFIED(qp) != NO) { + call smark (sp) + call salloc (qph, LEN_QPH, TY_STRUCT) + call aclri (Memi[qph], LEN_QPH) + + QPH_MAGIC(qph) = QP_MAGIC(qp) + QPH_VERSION(qph) = QPOE_VERSION + QPH_STOFFSET(qph) = LEN_QPH * SZ_STRUCT + 1 + + # The encoded QPOE header and symbol table are stored in a + # binary lfile in the datafile. + + fd = fm_fopen (QP_FM(qp), LF_QPOE, NEW_FILE, BINARY_FILE) + + # Update the QPOE file header. + n = LEN_QPH * SZ_STRUCT + call miipak32 (Memi[qph], Memi[qph], LEN_QPH, TY_STRUCT) + call write (fd, Memi[qph], n) + + # Update the symbol table. + call stsqueeze (QP_ST(qp)) + call stsave (QP_ST(qp), fd) + + QP_MODIFIED(qp) = NO + call close (fd) + call sfree (sp) + } + + # Update the datafile itself. + call fm_fcsync (QP_FM(qp)) +end diff --git a/sys/qpoe/qpwrite.x b/sys/qpoe/qpwrite.x new file mode 100644 index 00000000..dc5d4dab --- /dev/null +++ b/sys/qpoe/qpwrite.x @@ -0,0 +1,79 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "qpoe.h" + +# QP_WRITE -- Write to a range of elements in a parameter. Works for any +# parameter, including scalar parameters and both static and variable +# length array valued parameters. Automatic datatype conversion is +# performed for the primitive types. + +procedure qp_write (qp, param, buf, nelem, first, datatype) + +pointer qp #I QPOE descriptor +char param[ARB] #I parameter name +char buf[ARB] #I user data buffer containing data +int nelem #I number of data elements to write +int first #I first data element to write to +char datatype[ARB] #I datatype of input data + +pointer sp, fm, sym, tbuf, isym, osym +int fd, sz_itype, sz_otype, last, otype, itype +errchk qp_bind, qp_gpsym, fm_getfd, seek, syserrs +int fm_getfd(), qp_sizeof(), qp_dtype() +pointer qp_gpsym() + +begin + if (nelem <= 0) + return + + if (QP_ACTIVE(qp) == NO) + call qp_bind (qp) + + itype = qp_dtype (qp, datatype, isym) + fm = QP_FM(qp) + + # Lookup the symbol in the symbol table. + sym = qp_gpsym (qp, param) + if (sym == NULL) + call syserrs (SYS_QPUKNPAR, param) + else { + otype = S_DTYPE(sym) + osym = sym + } + + sz_itype = qp_sizeof (qp, itype, isym, INSTANCEOF) + sz_otype = qp_sizeof (qp, otype, osym, IMMEDIATE) + last = first + nelem - 1 + + # Check that the write is inbounds. + if (first <= 0 || (S_MAXELEM(sym) > 0 && last > S_MAXELEM(sym))) + call syserrs (SYS_QPINDXOOR, param) + + # Verify that any type conversion is legal. + if (otype != itype) + if (min(otype,itype) < TY_CHAR || max(otype,itype) > TY_DOUBLE) + call syserrs (SYS_QPBADCONV, param) + + # Open the lfile and update the data segment. + fd = fm_getfd (fm, S_LFILE(sym), READ_WRITE, 0) + call seek (fd, S_OFFSET(sym) + (first - 1) * sz_otype) + + # Output the data. + if (otype != itype) { + call smark (sp) + call salloc (tbuf, nelem * sz_otype, TY_CHAR) + call acht (buf, Memc[tbuf], nelem, itype, otype) + call write (fd, Memc[tbuf], nelem * sz_otype) + call sfree (sp) + } else + call write (fd, buf, nelem * sz_otype) + + # Update the array size if it got bigger. + if (last > S_NELEM(sym)) { + S_NELEM(sym) = last + QP_MODIFIED(qp) = YES + } + + call fm_retfd (fm, S_LFILE(sym)) +end diff --git a/sys/qpoe/zzdebug.x b/sys/qpoe/zzdebug.x new file mode 100644 index 00000000..5ca2de42 --- /dev/null +++ b/sys/qpoe/zzdebug.x @@ -0,0 +1,1696 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <mach.h> +include <ctype.h> +include <imhdr.h> +include <qpexset.h> +include <qpioset.h> +include <qpset.h> +include <gset.h> +include "qpoe.h" +include "qpex.h" +include "qpio.h" + +# ZZDEBUG -- Debug routines for the QPOE package. + +task parsei = t_parsei, # parse integer range list + parser = t_parser, # parse floating range list + qpparse = t_qpparse, # test qp_parse + tokens = t_tokens, # test get token, macro replacement + comp = t_comp, # test QPEX compile + expand = t_expand, # perform macro expansion on text + recio = t_recio, # test general record i/o + newcopy = t_newcopy, # test inheritance + syms = t_syms, # dump symbol table + hlist = t_hlist, # list file header + dumpevl = t_dumpevl, # dump event list descriptor + mkpoe = t_mkpoe, # convert CFA poefile to QPOE poefile + testpoe = t_testpoe, # make a test QPOE file + countpoe = t_countpoe, # count photons in regions + tfilter = t_tfilter, # verify time filtering + plotpoe = t_plotpoe, # read and plot photons + sum = t_sum, # sum counts in an image section + setwcs = t_setwcs, # store a wcs in a qpoe file + setmask = t_setmask, # set the default mask + mergei = t_mergei, # test merging of range lists + clear = t_clear # clear the screen + +define SZ_EXPR 256 +define SZ_OBUF 128000 +define SZ_TBUF 8 +define MAX_EVENTS 8192 +define SZ_RLBUF 512 + + +# PARSEI -- Test integer range list decoding and expression optimization. + +procedure t_parsei() + +pointer xs, xe +char lbuf[SZ_LINE], left[SZ_TBUF], right[SZ_TBUF] +int nranges, xlen, i1, i2, i + +int getline(), qpex_parsei() +bool streq() + +begin + xlen = 0 + + repeat { + # Get next expression. + call printf ("parse> ") + call flush (STDOUT) + if (getline (STDIN, lbuf) == EOF) + break + else if (streq (lbuf, "bye\n")) + break + + # Parse the expression. + nranges = qpex_parsei (lbuf, xs, xe, xlen) + + # List the ranges. + do i = 1, nranges { + i1 = Memi[xs+i-1] + i2 = Memi[xe+i-1] + + if (IS_LEFTI(i1)) + call strcpy ("LEFT", left, SZ_TBUF) + else { + call sprintf (left, SZ_TBUF, "%d") + call pargi (i1) + } + + if (IS_RIGHTI(i2)) + call strcpy ("RIGHT", right, SZ_TBUF) + else { + call sprintf (right, SZ_TBUF, "%d") + call pargi (i2) + } + + call printf ("%2d: %8s %8s\n") + call pargi (i) + call pargstr (left) + call pargstr (right) + } + + call flush (STDOUT) + } + + call mfree (xs, TY_INT) + call mfree (xe, TY_INT) +end + + +# PARSER -- Test real range list decoding and expression optimization. + +procedure t_parser() + +pointer xs, xe +char lbuf[SZ_LINE] +int nranges, xlen, i + +int getline(), qpex_parser() +bool streq() + +begin + xlen = 0 + + repeat { + # Get next expression. + call printf ("parse> ") + call flush (STDOUT) + if (getline (STDIN, lbuf) == EOF) + break + else if (streq (lbuf, "bye\n")) + break + + # Parse the expression. + nranges = qpex_parser (lbuf, xs, xe, xlen) + do i = 1, nranges { + call printf ("%2d: %7g %7g\n") + call pargi (i) + call pargr (Memr[xs+i-1]) + call pargr (Memr[xe+i-1]) + } + call flush (STDOUT) + } + + call mfree (xs, TY_REAL) + call mfree (xe, TY_REAL) +end + + +# QPPARSE -- Test qp_parse. + +procedure t_qpparse() + +char expr[SZ_LINE] +char root[SZ_FNAME] +char filter[SZ_FNAME] + +begin + call clgstr ("expr", expr, SZ_LINE) + call qp_parse (expr, root, SZ_FNAME, filter, SZ_FNAME) + + call printf ("root=`%s', filter=`%s'\n") + call pargstr (root) + call pargstr (filter) +end + + +# TOKENS -- Translate an input character stream into a stream of QPOE tokens. +# Macro replacement is performed on the input text. + +procedure t_tokens() + +char input[SZ_FNAME], refpoe[SZ_FNAME] +char tokbuf[SZ_FNAME], num[SZ_FNAME] + +pointer qp, in +int token, junk +int qp_gettok(), gltoc() +pointer qp_open, qp_opentext() + +begin + input[1] = '@' + call clgstr ("input", input[2], SZ_FNAME-1) + call clgstr ("refpoe", refpoe, SZ_FNAME) + + if (refpoe[1] != EOS) + qp = qp_open (refpoe, READ_ONLY, NULL) + else + qp = NULL + in = qp_opentext (qp, input) + + repeat { + token = qp_gettok (in, tokbuf, SZ_FNAME) + if (token != EOF) { + call printf ("%10s: %s\n") + switch (token) { + case TOK_IDENTIFIER: + call pargstr ("IDENT") + case TOK_NUMBER: + call pargstr ("NUMBER") + case TOK_STRING: + call pargstr ("STRING") + case TOK_COMMAND: + call pargstr ("COMMAND") + case TOK_PLUSEQUALS: + call pargstr ("PLUSEQ") + case TOK_COLONEQUALS: + call pargstr ("COLONEQ") + default: + junk = gltoc (token, num, SZ_FNAME, 8) + call pargstr (num) + } + if (IS_PRINT(tokbuf[1])) + call pargstr (tokbuf) + else + call pargstr ("") + } + } until (token == EOF) + + call printf ("EOF\n") + call flush (STDOUT) + + call qp_closetext (in) + if (qp != NULL) + call qp_close (qp) +end + + +# COMP -- Compile an expression with QPEX and print out the contents of +# the resultant descriptor, including the assembler translation of the +# expression. + +procedure t_comp() + +int out +pointer qp, ex +char text[SZ_LINE] +char output[SZ_FNAME] + +int open() +bool streq() +pointer qp_open(), qpex_open() + +begin + call clgstr ("poefile", text, SZ_LINE) + qp = qp_open (text, READ_ONLY, 0) + + call clgstr ("output", output, SZ_FNAME) + if (output[1] != EOS) + out = open (output, APPEND, TEXT_FILE) + else + out = NULL + + repeat { + call clgstr ("expr", text, SZ_LINE) + if (streq (text, "bye")) + break + else if (text[1] != EOS) { + ex = qpex_open (qp, text) + call qpex_debug (ex, STDOUT, QPEXD_SHOWALL) + if (out != NULL) { + call fprintf (out, "\f") + call qpex_debug (ex, out, QPEXD_SHOWALL) + } + call qpex_close (ex) + call flush (STDOUT) + } + } + + call close (out) + call qp_close (qp) +end + + +# EXPAND -- Perform macro expansion on text input by the user. + +procedure t_expand() + +pointer qp, sp, ip, text, obuf +int getline(), qp_expandtext(), strncmp(), clgeti() +pointer qp_open() + +begin + call smark (sp) + call salloc (text, SZ_LINE, TY_CHAR) + call salloc (obuf, SZ_OBUF, TY_CHAR) + + call clgstr ("poefile", Memc[text], SZ_LINE) + qp = qp_open (Memc[text], READ_ONLY, 0) + call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti("debug")) + + call printf ("Q> ") + call flush (STDOUT) + + while (getline (STDIN, Memc[text]) != EOF) { + for (ip=text; IS_WHITE(Memc[ip]); ip=ip+1) + ; + if (strncmp (Memc[ip], "bye", 3) == 0) + break + else if (Memc[ip] != '\n') { + call write (STDOUT, Memc[obuf], + qp_expandtext (qp, Memc[text], Memc[obuf], SZ_OBUF)) + call printf ("\n") + } + + call printf ("Q> ") + call flush (STDOUT) + } + + call qp_close (qp) + call sfree (sp) +end + + +# RECIO -- Test general record i/o. + +procedure t_recio() + +int i, n, nrec +pointer sp, qp, rp, poefile, data +int qp_read(), qp_accessf(), clgeti() +pointer qp_open() + +begin + call smark (sp) + call salloc (poefile, SZ_FNAME, TY_CHAR) + call salloc (data, 4096 * 3, TY_STRUCT) + + call clgstr ("poefile", Memc[poefile], SZ_FNAME) + qp = qp_open (Memc[poefile], READ_WRITE, 0) + call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti("debug")) + nrec = clgeti ("nrec") + + # Initialize the data array. + do i = 1, nrec { + rp = data + (i-1) * 3 + Memr[rp] = i + Memi[rp+1] = i + Mems[P2S(rp)+4] = i*10+1 + Mems[P2S(rp)+5] = i*10+2 + } + + if (qp_accessf (qp, "urec") == NO) + call qp_addf (qp, "urec", "{r,i,s,s}", 0, "User record type", 0) + if (qp_accessf (qp, "data") == NO) + call qp_addf (qp, "data", "urec", nrec, "User records", 0) + + # Initialize the parameter. + call qp_write (qp, "data", Memi[data], nrec, 1, "urec") + + call eprintf ("---------------- Full array read test:") + call aclri (Memi[data], nrec * 3) + n = qp_read (qp, "data", Memi[data], nrec, 1, "urec") + call eprintf (" n=%d\n"); call pargi(n) + do i = 1, nrec { + rp = data + (i-1) * 3 + call eprintf ("%8.1f %4d %4d %4d\n") + call pargr (Memr[rp]) + call pargi (Memi[rp+1]) + call pargs (Mems[P2S(rp)+4]) + call pargs (Mems[P2S(rp)+5]) + } + + call eprintf ("---------------- Array element read test:\n") + call aclri (Memi[data], nrec * 3) + do i = 1, nrec { + rp = data + (i-1) * 3 + n = qp_read (qp, "data", Memi[rp], 1, i, "urec") + call eprintf ("%4d %8.1f %4d %4d %4d\n") + call pargi (i) + call pargr (Memr[rp]) + call pargi (Memi[rp+1]) + call pargs (Mems[P2S(rp)+4]) + call pargs (Mems[P2S(rp)+5]) + } + + call eprintf ("---------------- Array element read test (reversed):\n") + call aclri (Memi[data], nrec * 3) + do i = nrec, 1, -1 { + rp = data + (i-1) * 3 + n = qp_read (qp, "data", Memi[rp], 1, i, "urec") + call eprintf ("%4d %8.1f %4d %4d %4d\n") + call pargi (i) + call pargr (Memr[rp]) + call pargi (Memi[rp+1]) + call pargs (Mems[P2S(rp)+4]) + call pargs (Mems[P2S(rp)+5]) + } + + call qp_close (qp) + call sfree (sp) +end + + +# NEWCOPY -- Test inheritance occurring during a new-copy open. + +procedure t_newcopy() + +char iname[SZ_FNAME] # input name +char oname[SZ_FNAME] # output name + +pointer iqp, oqp +pointer qp_open() + +begin + call clgstr ("input", iname, SZ_FNAME) + call clgstr ("output", oname, SZ_FNAME) + + iqp = qp_open (iname, READ_ONLY, NULL) + oqp = qp_open (oname, NEW_COPY, iqp) + + call printf ("iqp=%x; oqp=%x\n") + call pargi (iqp) + call pargi (oqp) + + call qp_close (oqp) + call qp_close (iqp) +end + + +# SYMS -- Dump the symbol table of a QPOE datafile. + +procedure t_syms() + +char fname[SZ_FNAME] +pointer qp, qp_open() + +begin + call clgstr ("fname", fname, SZ_FNAME) + qp = qp_open (fname, READ_ONLY, 0) + call qp_dsym (qp, STDOUT) + call qp_close (qp) +end + + +# HLIST -- List selected header parameters. + +procedure t_hlist() + +pointer qp, list, sym +int nelem, maxelem, flags +char datatype[SZ_DATATYPE], comment[SZ_COMMENT] +char fname[SZ_FNAME], param[SZ_FNAME], pattern[SZ_FNAME] +pointer qp_open(), qp_ofnlu(), qp_ofnls(), qp_gpsym() +int qp_queryf(), qp_gnfn() +bool clgetb() + +begin + call clgstr ("fname", fname, SZ_FNAME) + call clgstr ("pattern", pattern, SZ_FNAME) + + qp = qp_open (fname, READ_ONLY, 0) + if (clgetb ("sort")) + list = qp_ofnls (qp, pattern) + else + list = qp_ofnlu (qp, pattern) + + call printf (" PARAM DTYPE NELEM MAXEL LF OFF FLG COMMENT\n") + while (qp_gnfn (list, param, SZ_FNAME) != EOF) { + nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags) + sym = qp_gpsym (qp, param) + + call printf ("%15s %6s %5d %5d %2d%4d %3o %s\n") + call pargstr (param) + call pargstr (datatype) + call pargi (nelem) + call pargi (maxelem) + if (sym != NULL) { + call pargi (S_LFILE(sym)) + call pargi (S_OFFSET(sym)) + call pargi (and (flags, 777B)) + call pargstr (comment) + } else { + call pargi (0) + call pargi (0) + call pargi (0) + call pargstr ("[could not find symbol (macro?)]") + } + } + + call qp_cfnl (list) + call qp_close (qp) +end + + +# DUMPEVL -- Dump an event list descriptor. + +procedure t_dumpevl() + +pointer qp, io, dd, ev +char poefile[SZ_FNAME], param[SZ_FNAME] +char datatype[SZ_DATATYPE], comment[SZ_COMMENT] +int offset, dtype, size, nelem, maxelem, flags, i, j +pointer qp_open(), qpio_open(), qpio_stati(), coerce() +int qp_queryf(), sizeof() + +begin + call clgstr ("poefile", poefile, SZ_FNAME) + qp = qp_open (poefile, READ_ONLY, NULL) + + call clgstr ("eventlist", param, SZ_FNAME) + if (param[1] == EOS) + call strcpy ("events", param, SZ_FNAME) + io = qpio_open (qp, param, READ_ONLY) + + call printf ("%s.%s:\n") + call pargstr (poefile) + call pargstr (param) + + nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags) + call printf ("dtype=%s nelem=%d maxel=%d flg=%o comment=%s\n") + call pargstr (datatype) + call pargi (nelem) + call pargi (maxelem) + call pargi (and (flags, 777B)) + call pargstr (comment) + + call printf ("%s=%dx%d ") + call pargstr ("blockfactor") + call pargi (qpio_stati(io, QPIO_XBLOCKFACTOR)) + call pargi (qpio_stati(io, QPIO_YBLOCKFACTOR)) + call printf ("%s=%d ") + call pargstr ("bucketlen") + call pargi (qpio_stati(io, QPIO_BUCKETLEN)) + call printf ("%s=%d ") + call pargstr ("debug") + call pargi (qpio_stati(io, QPIO_DEBUG)) + call printf ("%s=%d ") + call pargstr ("evxoff") + call pargi (qpio_stati(io, QPIO_EVXOFF)) + call printf ("%s=%d ") + call pargstr ("evxtype") + call pargi (qpio_stati(io, QPIO_EVXTYPE)) + call printf ("%s=%d ") + call pargstr ("evyoff") + call pargi (qpio_stati(io, QPIO_EVYOFF)) + call printf ("%s=%d ") + call pargstr ("evytype") + call pargi (qpio_stati(io, QPIO_EVYTYPE)) + call printf ("\n") + + call printf ("%s=%xX ") + call pargstr ("ex") + call pargi (qpio_stati(io, QPIO_EX)) + call printf ("%s=%d ") + call pargstr ("noindex") + call pargi (qpio_stati(io, QPIO_NOINDEX)) + call printf ("%s=%d ") + call pargstr ("optbufsize") + call pargi (qpio_stati(io, QPIO_OPTBUFSIZE)) + call printf ("%s=%xX ") + call pargstr ("pl") + call pargi (qpio_stati(io, QPIO_PL)) + call printf ("%s=%d ") + call pargstr ("eventlen") + call pargi (qpio_stati(io, QPIO_EVENTLEN)) + call printf ("%s=%d ") + call pargstr ("fd") + call pargi (qpio_stati(io, QPIO_FD)) + call printf ("\n") + + call printf ("%s=%d ") + call pargstr ("indexlen") + call pargi (qpio_stati(io, QPIO_INDEXLEN)) + call printf ("%s=%d ") + call pargstr ("ixxoff") + call pargi (qpio_stati(io, QPIO_IXXOFF)) + call printf ("%s=%d ") + call pargstr ("ixxtype") + call pargi (qpio_stati(io, QPIO_IXXTYPE)) + call printf ("%s=%d ") + call pargstr ("ixyoff") + call pargi (qpio_stati(io, QPIO_IXYOFF)) + call printf ("%s=%d ") + call pargstr ("ixytype") + call pargi (qpio_stati(io, QPIO_IXYTYPE)) + call printf ("%s=%d ") + call pargstr ("lf") + call pargi (qpio_stati(io, QPIO_LF)) + call printf ("%s=%xX ") + call pargstr ("maskp") + call pargi (qpio_stati(io, QPIO_MASKP)) + call printf ("\n") + + call printf ("%s=%xX ") + call pargstr ("maxevp") + call pargi (qpio_stati(io, QPIO_MAXEVP)) + call printf ("%s=%xX ") + call pargstr ("minevp") + call pargi (qpio_stati(io, QPIO_MINEVP)) + call printf ("%s=%d ") + call pargstr ("ncols") + call pargi (qpio_stati(io, QPIO_NCOLS)) + call printf ("%s=%d ") + call pargstr ("nlines") + call pargi (qpio_stati(io, QPIO_NLINES)) + call printf ("%s=%xX ") + call pargstr ("paramp") + call pargi (qpio_stati(io, QPIO_PARAMP)) + call printf ("%s=%xX ") + call pargstr ("qp") + call pargi (qpio_stati(io, QPIO_QP)) + call printf ("\n") + + # Print domain attributes. + dd = IO_DD(io) + call printf ("Domain `%s': len=%d nfields=%d xfield=%d yfield=%d\n") + call pargstr (datatype) + call pargi (DD_STRUCTLEN(dd)) + call pargi (DD_NFIELDS(dd)) + call pargi (DD_XFIELD(dd)) + call pargi (DD_YFIELD(dd)) + + # Print min/max evl records. + do j = 1, 2 { + if (j == 1) { + call printf ("minevl: ") + ev = qpio_stati (io, QPIO_MINEVP) + } else { + call printf ("maxevl: ") + ev = qpio_stati (io, QPIO_MAXEVP) + } + + do i = 1, DD_NFIELDS(dd) { + offset = DD_FOFFSET(dd,i) + dtype = DD_FTYPE(dd,i) + size = sizeof(dtype) + + switch (dtype) { + case TY_SHORT: + call printf (" s%d=%d") + call pargi (offset * size * SZB_CHAR) + call pargs (Mems[coerce(ev,TY_SHORT,dtype) + offset]) + case TY_INT: + call printf (" i%d=%d") + call pargi (offset * size * SZB_CHAR) + call pargi (Memi[coerce(ev,TY_SHORT,dtype) + offset]) + case TY_LONG: + call printf (" l%d=%d") + call pargi (offset * size * SZB_CHAR) + call pargl (Meml[coerce(ev,TY_SHORT,dtype) + offset]) + case TY_REAL: + call printf (" r%d=%0.5g") + call pargi (offset * size * SZB_CHAR) + call pargr (Memr[coerce(ev,TY_SHORT,dtype) + offset]) + case TY_DOUBLE: + call printf (" d%d=%0.8g") + call pargi (offset * size * SZB_CHAR) + call pargd (Memd[coerce(ev,TY_SHORT,dtype) + offset]) + default: + call printf (" type=%d") + call pargi (dtype) + } + } + + call printf ("\n") + } +end + + +# MKPOE -- Convert CFA poefile to QPOE poefile. +# ------------------------- + +# Size limiting defintions. +define LEN_EVBUF 512 # size of output event buffer +define LEN_CVBUF 1000 # max number of mask regions +define SZ_KEY 20 + +# CFA Poefile definitions. +define SZ_EVENT SZ_OEVENT +define SZ_IEVENT 10 # size of input event struct, chars +define SZ_OEVENT 12 # size of output event struct, chars +define SZ_FILEHEADER 256 # size of file header, chars + +# File header fields of interest. +define O_MISSION 1 # byte offset of "mission" field +define T_MISSION TY_SHORT # datatype of mission field +define O_INSTRUMENT 1 # byte offset of "instrument" field +define T_INSTRUMENT TY_SHORT # datatype of instrument field +define O_XDIM 129 # byte offset of Xdim field +define T_XDIM TY_SHORT # datatype of Xdim field +define O_YDIM 131 # byte offset of Ydim field +define T_YDIM TY_SHORT # datatype of Ydim field +define O_PSTART 505 # byte offset of PhotonStart field +define T_PSTART TY_LONG # datatype of PhotonStart field +define O_PSTOP 509 # byte offset of PhotonStop field +define T_PSTOP TY_LONG # datatype of PhotonStop field + +# Input event struct fields of interest. +define O_X 1 # sky coordinates +define T_X TY_SHORT +define O_Y 3 +define T_Y TY_SHORT +define O_TIME 5 # arrival time +define T_TIME TY_DOUBLE +define O_PHA 13 # pulse height +define T_PHA TY_SHORT +define O_PI 15 # energy +define T_PI TY_SHORT +define O_DX 17 # detector coordinates +define T_DX TY_SHORT +define O_DY 19 +define T_DY TY_SHORT + +# The event struct to be stored in the QPOE file. +define EVTYPE "event" +define FIELDLIST "{s:x,s:y,s,s,d,s,s}" + +define EV_X Mems[$1] +define EV_Y Mems[$1+1] +define EV_PHA Mems[$1+2] +define EV_PI Mems[$1+3] +define EV_TIME Memd[($1+4-1)/SZ_DOUBLE+1] +define EV_DX Mems[$1+8] +define EV_DY Mems[$1+9] + +# Define an event structure with short coordinates +define S_FIELDLIST "{s:x,s:y,s,s,d,s,s}" +define S_SZ_EVENT 10 +define S_EV_X Mems[$1] +define S_EV_Y Mems[$1+1] +define S_EV_PHA Mems[$1+2] +define S_EV_PI Mems[$1+3] +define S_EV_TIME Memd[($1+4-1)/SZ_DOUBLE+1] +define S_EV_DX Mems[$1+8] +define S_EV_DY Mems[$1+9] + +# Define an event structure with integer coordinates +define I_FIELDLIST "{d,i:x,i:y,i,i,s,s}" +define I_SZ_EVENT 14 +define I_EV_TIME Memd[($1-1)/SZ_DOUBLE+1] +define I_EV_X Memi[($1+4-1)/SZ_INT+1] +define I_EV_Y Memi[($1+6-1)/SZ_INT+1] +define I_EV_DX Memi[($1+8-1)/SZ_INT+1] +define I_EV_DY Memi[($1+10-1)/SZ_INT+1] +define I_EV_PHA Mems[$1+12] +define I_EV_PI Mems[$1+13] + +# Define an event structure with real coordinates +define R_FIELDLIST "{d,r:x,r:y,r,r,s,s}" +define R_SZ_EVENT 14 +define R_EV_TIME Memd[($1-1)/SZ_DOUBLE+1] +define R_EV_X Memr[($1+4-1)/SZ_REAL+1] +define R_EV_Y Memr[($1+6-1)/SZ_REAL+1] +define R_EV_DX Memr[($1+8-1)/SZ_REAL+1] +define R_EV_DY Memr[($1+10-1)/SZ_REAL+1] +define R_EV_PHA Mems[$1+12] +define R_EV_PI Mems[$1+13] + +# Define an event structure with double coordinates +define D_FIELDLIST "{d,d:x,d:y,d,d,s,s}" +define D_SZ_EVENT 22 +define D_EV_TIME Memd[($1-1)/SZ_DOUBLE+1] +define D_EV_X Memd[($1+4-1)/SZ_DOUBLE+1] +define D_EV_Y Memd[($1+8-1)/SZ_DOUBLE+1] +define D_EV_DX Memd[($1+12-1)/SZ_DOUBLE+1] +define D_EV_DY Memd[($1+16-1)/SZ_DOUBLE+1] +define D_EV_PHA Mems[$1+20] +define D_EV_PI Mems[$1+21] + + +# MKPOE -- Write out a new POEFILE, taking a CFA POE file as input. +# The input file uses big-endian format for integers, and IEEE for floats. +# The input file must be sorted in order for the output file to be indexed. + +procedure t_mkpoe() + +char infile[SZ_FNAME] # input CFA-format poefile +char outfile[SZ_FNAME] # output QPOE-format poefile + +char key[SZ_KEY] +pointer sp, hdr, obuf, optr, ph, ev, qp, io +int datastart, dataend, mission, instrument, now +int debug, nphotons, naxes, axlen[2], dmin[8], dmax[8], in, op, i + +bool clgetb() +double mp_getd() +pointer qp_open(), qpio_open() +int open(), read(), mp_geti(), clgeti(), clktime() + +begin + call smark (sp) + call salloc (hdr, SZ_FILEHEADER, TY_CHAR) + call salloc (obuf, LEN_EVBUF * SZ_OEVENT / SZ_SHORT, TY_SHORT) + call salloc (optr, LEN_EVBUF, TY_POINTER) + call salloc (ph, SZ_IEVENT, TY_CHAR) + + call clgstr ("infile", infile, SZ_FNAME) + call clgstr ("outfile", outfile, SZ_FNAME) + + # Open the input and output files. Clobber the output file if + # it already exists. + + in = open (infile, READ_ONLY, BINARY_FILE) + iferr (call qp_delete (outfile)) + ; + qp = qp_open (outfile, NEW_FILE, NULL) + + # Set the datafile page size. + call qp_seti (qp, QPOE_PAGESIZE, clgeti("pagesize")) + + # Set the bucket length in units of number of events. + call qp_seti (qp, QPOE_BUCKETLEN, clgeti("bucketlen")) + + # Set the debug level. + debug = clgeti ("debug") + call qp_seti (qp, QPOE_DEBUGLEVEL, debug) + + # Read and decode the input file header. + if (read (in, Memc[hdr], SZ_FILEHEADER) < SZ_FILEHEADER) + call error (1, "cannot read input file header") + + naxes = 2 + axlen[1] = mp_geti (Memc[hdr], O_XDIM, T_XDIM) + axlen[2] = mp_geti (Memc[hdr], O_YDIM, T_YDIM) + datastart = mp_geti (Memc[hdr], O_PSTART, T_PSTART) + dataend = mp_geti (Memc[hdr], O_PSTOP, T_PSTOP) + nphotons = (dataend - datastart + 1) / (SZ_IEVENT * SZB_CHAR) + + mission = mp_geti (Memc[hdr], O_MISSION, T_MISSION) + instrument = mp_geti (Memc[hdr], O_INSTRUMENT, T_INSTRUMENT) + + call eprintf ("xdim=%d, ydim=%d, datastart=%d, nphotons=%d\n") + call pargi (axlen[1]) + call pargi (axlen[2]) + call pargi (datastart) + call pargi (nphotons) + + # Setup the QPOE file header. + call qp_addf (qp, "naxes", "i", 1, "number of image axes", 0) + call qp_puti (qp, "naxes", naxes) + call qp_addf (qp, "axlen", "i", 2, "length of each axis", 0) + call qp_write (qp, "axlen", axlen, 2, 1, "i") + + now = clktime(0) + call qp_addf (qp, "cretime", "i", 1, "image creation time", 0) + call qp_puti (qp, "cretime", now) + call qp_addf (qp, "modtime", "i", 1, "image modify time", 0) + call qp_puti (qp, "modtime", now) + call qp_addf (qp, "limtime", "i", 1, "data min/max update time", 0) + call qp_puti (qp, "limtime", now) + + # Invent some data min/max values for now. + do i = 1, 8 { + dmin[i] = 0 + dmax[i] = 64 + } + call qp_addf (qp, "datamin", "i", 8, "minimum pixel value", 0) + call qp_write (qp, "datamin", dmin, 8, 1, "i") + call qp_addf (qp, "datamax", "i", 8, "maximum pixel value", 0) + call qp_write (qp, "datamax", dmax, 8, 1, "i") + + # Throw in a few miscellaneous params for testing purposes. + call qp_addf (qp, "mission", "s", 1, "mission type code", 0) + call qp_puti (qp, "mission", mission) + call qp_addf (qp, "instrument", "s", 1, "instrument type code", 0) + call qp_puti (qp, "instrument", instrument) + + # Define the event structure for the QPOE output file. + call qp_addf (qp, EVTYPE, FIELDLIST, 1, "event record type", 0) + + # Copy the event (photon) list. + call qp_addf (qp, "events", "event", 0, "main event list", 0) + io = qpio_open (qp, "events", NEW_FILE) + call seek (in, datastart / SZB_CHAR + 1) + op = 0 + + do i = 1, nphotons { + # Read next event. + if (read (in, Memc[ph], SZ_IEVENT) < SZ_IEVENT) + call error (2, "photon event list truncated") + + # Copy/transform event struct (not very efficient, but this + # is only debug code). + + ev = obuf + (op * SZ_OEVENT / SZ_SHORT) + Memi[optr+op] = ev + + EV_TIME(ev) = mp_getd (Memc[ph], O_TIME, T_TIME) + EV_X(ev) = mp_geti (Memc[ph], O_X, T_X) + EV_Y(ev) = mp_geti (Memc[ph], O_Y, T_Y) + EV_PHA(ev) = mp_geti (Memc[ph], O_PHA, T_PHA) + EV_PI(ev) = mp_geti (Memc[ph], O_PI, T_PI) + EV_DX(ev) = mp_geti (Memc[ph], O_DX, T_DX) + EV_DY(ev) = mp_geti (Memc[ph], O_DY, T_DY) + + if (debug > 4) { + call eprintf ("%4d %4d %4d %4d %7d %7d %g\n") + call pargs (EV_X(ev)) + call pargs (EV_Y(ev)) + call pargs (EV_DX(ev)) + call pargs (EV_DY(ev)) + call pargs (EV_PHA(ev)) + call pargs (EV_PI(ev)) + call pargd (EV_TIME(ev)) + } + + # Bump output pointer and flush output buffer when it fills. + op = op + 1 + if (op >= LEN_EVBUF) { + call qpio_putevents (io, Memi[optr], op) + op = 0 + } + } + + # Flush any remaining buffered data. + if (op > 0) + call qpio_putevents (io, Memi[optr], op) + + # Construct index. + if (clgetb ("mkindex")) { + call clgstr ("key", key, SZ_KEY) + call qpio_mkindex (io, key) + } + + # Clean up. + call qpio_close (io) + call qp_close (qp) + call close (in) + + call sfree (sp) +end + + +# MP_GETI -- Get an integer field from the raw input data. + +int procedure mp_geti (buf, boffset, dtype) + +char buf[ARB] # byte-stream data buffer +int boffset # byte offset of desired field +int dtype # datatype of stored field + +short sval +int nbytes, ival +int sizeof() + +begin + nbytes = sizeof(dtype) * SZB_CHAR + + switch (dtype) { + case TY_SHORT: + if (BYTE_SWAP2 == YES) + call bswap2 (buf, boffset, sval, 1, nbytes) + else + call bytmov (buf, boffset, sval, 1, nbytes) + return (sval) + case TY_INT, TY_LONG: + if (BYTE_SWAP4 == YES) + call bswap4 (buf, boffset, ival, 1, nbytes) + else + call bytmov (buf, boffset, ival, 1, nbytes) + return (ival) + default: + call error (3, "bad dtype switch in mp_geti") + } +end + + +# MP_GETD -- Get a double field from the raw input data. We assume that both +# the input and output are IEEE floating, hence all we are really doing here +# is providing for arbitrarily aligned fields, and providing type conversion. + +double procedure mp_getd (buf, boffset, dtype) + +char buf[ARB] # byte-stream data buffer +int boffset # byte offset of desired field +int dtype # datatype of stored field + +double dval +real rval +int nbytes, half +int sizeof() + +begin + nbytes = sizeof(dtype) * SZB_CHAR + half = nbytes / 2 + + switch (dtype) { + case TY_REAL: + if (BYTE_SWAP4 == YES) + call bswap4 (buf, boffset, rval, 1, nbytes) + else + call bytmov (buf, boffset, rval, 1, nbytes) + return (rval) + case TY_DOUBLE: + if (BYTE_SWAP4 == YES) { + call bswap4 (buf, boffset, dval, 1+half, half) + call bswap4 (buf, boffset+half, dval, 1, half) + } else + call bytmov (buf, boffset, dval, 1, nbytes) + return (dval) + default: + call error (3, "bad dtype switch in mp_getd") + } +end + + +# TESTPOE -- Make a test QPOE file, generating an artificial sequence of +# events (for testing special cases). The event list is not sorted or +# indexed. + +procedure t_testpoe() + +char outfile[SZ_FNAME] # output QPOE-format poefile + +pointer sp, ev, qp, io, evl[1] +int nevents, naxes, axlen[2], i, datatype +pointer qp_open(), qpio_open() +int clgeti() +char clgetc() + +begin + # Open the output file. + call clgstr ("outfile", outfile, SZ_FNAME) + iferr (call qp_delete (outfile)) + ; + qp = qp_open (outfile, NEW_FILE, NULL) + + naxes = 2 + axlen[1] = clgeti ("ncols") + axlen[2] = clgeti ("nlines") + datatype = clgetc ("datatype") + + # Setup the QPOE file header. + call qp_addf (qp, "naxes", "i", 1, "number of image axes", 0) + call qp_puti (qp, "naxes", naxes) + call qp_addf (qp, "axlen", "i", 2, "length of each axis", 0) + call qp_write (qp, "axlen", axlen, 2, 1, "i") + + # Define the event structure for the QPOE output file. + call smark (sp) + switch (datatype) { + case 's': + call salloc (ev, S_SZ_EVENT / SZ_SHORT, TY_SHORT) + call qp_addf (qp, EVTYPE, S_FIELDLIST, 1, "event record type", 0) + case 'i': + call salloc (ev, I_SZ_EVENT / SZ_SHORT, TY_SHORT) + call qp_addf (qp, EVTYPE, I_FIELDLIST, 1, "event record type", 0) + case 'r': + call salloc (ev, R_SZ_EVENT / SZ_SHORT, TY_SHORT) + call qp_addf (qp, EVTYPE, R_FIELDLIST, 1, "event record type", 0) + case 'd': + call salloc (ev, D_SZ_EVENT / SZ_SHORT, TY_SHORT) + call qp_addf (qp, EVTYPE, D_FIELDLIST, 1, "event record type", 0) + } + + + # Copy the event (photon) list. + call qp_addf (qp, "events", "event", 0, "main event list", 0) + io = qpio_open (qp, "events", NEW_FILE) + + # Generate some dummy events. + nevents = clgeti ("nevents") + evl[1] = ev + + # Hack this to generate different types of test files. + do i = 1, nevents { + switch (datatype) { + + case 's': + S_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0 + if (mod(i,2) == 0) { + S_EV_X(ev) = (i - 1) * 10 + 1 + S_EV_Y(ev) = (i - 1) * 10 + 1 + } else { + S_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1) + S_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1) + } + S_EV_PHA(ev) = mod (nint(i * 11.1111), 20) + S_EV_PI(ev) = i / 2 + S_EV_DX(ev) = nevents - i + 1 + S_EV_DY(ev) = nevents - i + 1 + + case 'i': + I_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0 + if (mod(i,2) == 0) { + I_EV_X(ev) = (i - 1) * 10 + 1 + I_EV_Y(ev) = (i - 1) * 10 + 1 + } else { + I_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1) + I_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1) + } + I_EV_PHA(ev) = mod (nint(i * 11.1111), 20) + I_EV_PI(ev) = i / 2 + I_EV_DX(ev) = nevents - i + 1 + I_EV_DY(ev) = nevents - i + 1 + + case 'r': + R_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0 + if (mod(i,2) == 0) { + R_EV_X(ev) = (i - 1) * 10 + 1 + R_EV_Y(ev) = (i - 1) * 10 + 1 + } else { + R_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1) + R_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1) + } + R_EV_PHA(ev) = mod (nint(i * 11.1111), 20) + R_EV_PI(ev) = i / 2 + R_EV_DX(ev) = nevents - i + 1 + R_EV_DY(ev) = nevents - i + 1 + + case 'd': + D_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0 + if (mod(i,2) == 0) { + D_EV_X(ev) = (i - 1) * 10 + 1 + D_EV_Y(ev) = (i - 1) * 10 + 1 + } else { + D_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1) + D_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1) + } + D_EV_PHA(ev) = mod (nint(i * 11.1111), 20) + D_EV_PI(ev) = i / 2 + D_EV_DX(ev) = nevents - i + 1 + D_EV_DY(ev) = nevents - i + 1 + + } + + call qpio_putevents (io, evl, 1) + } + + # Clean up. + call qpio_close (io) + call qp_close (qp) + call sfree (sp) +end + + +# COUNTPOE -- Count photons in regions. Whether or not there are any regions +# depends upon whether the user specifies a region mask, or upon whether the +# image has a default mask. If there is no mask the entire image is counted. +# If the user specifies a filter then event attribute filtering will be +# performed as well. Mask region values should be restricted to the range +# 0-999. + +procedure t_countpoe() + +bool list_events +int debug, nev, mval, m, i +pointer sp, qp, poefile, evlist, evl, cv, ev, io + +bool clgetb() +pointer qp_open(), qpio_open() +int qpio_getevents(), clgeti() + +begin + call smark (sp) + call salloc (poefile, SZ_FNAME, TY_CHAR) + call salloc (evlist, SZ_EXPR, TY_CHAR) + call salloc (evl, LEN_EVBUF, TY_POINTER) + call salloc (cv, LEN_CVBUF, TY_INT) + + call clgstr ("poefile", Memc[poefile], SZ_FNAME) + qp = qp_open (Memc[poefile], READ_ONLY, NULL) + + debug = clgeti ("debug") + call qp_seti (qp, QPOE_DEBUGLEVEL, debug) + + call clgstr ("eventlist", Memc[evlist], SZ_EXPR) + io = qpio_open (qp, Memc[evlist], READ_ONLY) + + list_events = clgetb ("list_events") + if (list_events) + call printf (" EV X Y DX DY PHA PI TIME\n") + + call aclri (Memi[cv], LEN_CVBUF) + + # Scan the event list. + while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) { + if (list_events) { + do i = 1, nev { + ev = Memi[evl+i-1] + call printf ("%4d %4d %4d %4d %4d %7d %7d %g\n") + call pargi (i) + call pargs (EV_X(ev)) + call pargs (EV_Y(ev)) + call pargs (EV_DX(ev)) + call pargs (EV_DY(ev)) + call pargs (EV_PHA(ev)) + call pargs (EV_PI(ev)) + call pargd (EV_TIME(ev)) + } + } + + m = min (LEN_CVBUF, mval) + Memi[cv+m] = Memi[cv+m] + nev + } + + call qpio_close (io) + + # Print the count of the number of photons in each region. + if (list_events) + call printf ("\n") + + call printf ("REGION: ") + do i = 0, LEN_CVBUF-1 + if (Memi[cv+i] > 0) { + call printf (" %6oB") + call pargi (i) + } + call printf ("\n") + + call printf ("COUNTS: ") + do i = 0, LEN_CVBUF-1 + if (Memi[cv+i] > 0) { + call printf (" %7d") + call pargi (Memi[cv+i]) + } + call printf ("\n") + + call qp_close (qp) + call sfree (sp) +end + + +# TFILTER -- Perform a brute force time filtering operation upon an event +# list, and compare against the results of a standard QPEX time filter. +# This is used to verify the operation of the optimized QPEX time filter. + +procedure t_tfilter() + +bool open_left, open_right, pass +int nev1, nev2, totev, mval, nev, xlen, nranges, fd1, fd2, i, j +pointer sp, poefile, filter, output, fname, evl, x1, y1, t1, x2, y2, t2 +pointer qp, io, ex, ev, xs, xe +double t + +bool clgetb() +int qpio_getevents(), qpex_attrld(), open() +pointer qp_open(), qpio_open(), qpex_open() + +begin + call smark (sp) + call salloc (poefile, SZ_FNAME, TY_CHAR) + call salloc (output, SZ_FNAME, TY_CHAR) + call salloc (fname, SZ_FNAME, TY_CHAR) + call salloc (filter, SZ_EXPR, TY_CHAR) + call salloc (evl, LEN_EVBUF, TY_POINTER) + + call salloc (x1, MAX_EVENTS, TY_SHORT) + call salloc (y1, MAX_EVENTS, TY_SHORT) + call salloc (t1, MAX_EVENTS, TY_DOUBLE) + call salloc (x2, MAX_EVENTS, TY_SHORT) + call salloc (y2, MAX_EVENTS, TY_SHORT) + call salloc (t2, MAX_EVENTS, TY_DOUBLE) + + nev1 = 0 + nev2 = 0 + totev = 0 + + call clgstr ("poefile", Memc[poefile], SZ_FNAME) + qp = qp_open (Memc[poefile], READ_ONLY, NULL) + io = qpio_open (qp, "", READ_ONLY) + + call clgstr ("filter", Memc[filter], SZ_EXPR) + ex = qpex_open (qp, Memc[filter]) + + call clgstr ("output", Memc[output], SZ_FNAME) + + if (clgetb ("showfilter")) { + call qpex_debug (ex, STDOUT, QPEXD_SHOWALL) + call flush (STDOUT) + } + + # Scan the event list using the given filter. + call printf ("scan event list using optimized filter: ") + call flush (STDOUT) + + call qpio_seti (io, QPIO_EX, ex) + while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) { + do i = 1, nev { + ev = Memi[evl+i-1] + Mems[x1+nev1] = EV_X(ev) + Mems[y1+nev1] = EV_Y(ev) + Memd[t1+nev1] = EV_TIME(ev) + nev1 = min (MAX_EVENTS, nev1 + 1) + } + } + + call printf ("%d events\n") + call pargi (nev1) + xlen = 128 + call malloc (xs, TY_DOUBLE, xlen) + call malloc (xe, TY_DOUBLE, xlen) + + # Get the time filter as a list of ranges. + xs = NULL; xe = NULL; xlen = 0 + nranges = qpex_attrld (ex, "time", xs, xe, xlen) + if (nranges > 0) { + open_left = IS_INDEF(Memd[xs]) + open_right = IS_INDEF(Memd[xe+nranges-1]) + } else { + open_left = false + open_right = false + } + + # Scan the event list, applying a brute force time filter. + call qpio_seti (io, QPIO_EX, NULL) + call printf ("scan event list using brute force filter: ") + call flush (STDOUT) + + while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) { + do i = 1, nev { + ev = Memi[evl+i-1] + t = EV_TIME(ev) + + # Apply the time filter. + if (open_left && open_right && nranges == 1) + pass = true + else { + pass = false + do j = 1, nranges { + if (j == 1 && open_left) { + if (t <= Memd[xe]) { + pass = true + break + } + } else if (j == nranges && open_right) { + if (t >= Memd[xs+nranges-1]) { + pass = true + break + } + } else if (t >= Memd[xs+j-1] && t <= Memd[xe+j-1]) { + pass = true + break + } + } + } + + if (pass) { + Mems[x2+nev2] = EV_X(ev) + Mems[y2+nev2] = EV_Y(ev) + Memd[t2+nev2] = EV_TIME(ev) + nev2 = min (MAX_EVENTS, nev2 + 1) + } + } + + totev = totev + nev + } + + call printf ("%d events\n") + call pargi (nev2) + call printf ("out of a total of %d events\n") + call pargi (totev) + call flush (STDOUT) + + # Dump the two event lists if an output root filename was given. + if (Memc[output] != EOS) { + call sprintf (Memc[fname], SZ_FNAME, "%s.1") + call pargstr (Memc[output]) + iferr (call delete (Memc[fname])) + ; + fd1 = open (Memc[fname], NEW_FILE, TEXT_FILE) + + call sprintf (Memc[fname], SZ_FNAME, "%s.2") + call pargstr (Memc[output]) + iferr (call delete (Memc[fname])) + ; + fd2 = open (Memc[fname], NEW_FILE, TEXT_FILE) + + do i = 1, max (nev1, nev2) { + if (i <= nev1) { + call fprintf (fd1, "%d %d %g\n") + call pargs (Mems[x1+i-1]) + call pargs (Mems[y1+i-1]) + call pargd (Memd[t1+i-1]) + } + if (i <= nev2) { + call fprintf (fd2, "%d %d %g\n") + call pargs (Mems[x2+i-1]) + call pargs (Mems[y2+i-1]) + call pargd (Memd[t2+i-1]) + } + } + + call close (fd1) + call close (fd2) + } + + # Compare the results of the two filters for equality. + pass = true + do i = 1, min (nev1, nev2) { + if (Mems[x1+i-1] != Mems[x2+i-1] || Mems[y1+i-1] != Mems[y2+i-1]) { + call printf ("bad compare at event %d: ") + call pargi (i) + call printf ("[%d,%d,%0.4f] != [%d,%d,%0.4f]\n") + call pargs (Mems[x1+i-1]) + call pargs (Mems[y1+i-1]) + call pargd (Memd[t1+i-1]) + call pargs (Mems[x2+i-1]) + call pargs (Mems[y2+i-1]) + call pargd (Memd[t2+i-1]) + pass = false + break + } + } + + if (pass) { + call printf ("first %d events are identical\n") + call pargi (min (nev1, nev2)) + } + + call mfree (xs, TY_DOUBLE) + call mfree (xe, TY_DOUBLE) + call qpex_close (ex) + call qpio_close (io) + call qp_close (qp) + + call sfree (sp) +end + + +# PLOTPOE -- Read and plot photons, showing the position of each photon +# in the image matrix, according to the current coordinate system. + +procedure t_plotpoe() + +int ncols, nlines, xblock, yblock, mval, nev, i +pointer sp, poefile, evlist, evl, xv, yv, qp, io, ev, gp +pointer qp_open(), gopen(), qpio_open +int clgeti(), qp_stati(), qp_geti(), qpio_getevents() + +begin + call smark (sp) + call salloc (poefile, SZ_FNAME, TY_CHAR) + call salloc (evlist, SZ_EXPR, TY_CHAR) + call salloc (evl, LEN_EVBUF, TY_POINTER) + call salloc (xv, LEN_EVBUF, TY_REAL) + call salloc (yv, LEN_EVBUF, TY_REAL) + + call clgstr ("poefile", Memc[poefile], SZ_FNAME) + qp = qp_open (Memc[poefile], READ_ONLY, NULL) + + call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti ("debug")) + call qp_seti (qp, QPOE_XBLOCKFACTOR, clgeti ("xblock")) + call qp_seti (qp, QPOE_YBLOCKFACTOR, clgeti ("yblock")) + + xblock = qp_stati (qp, QPOE_XBLOCKFACTOR) + yblock = qp_stati (qp, QPOE_YBLOCKFACTOR) + ncols = qp_geti (qp, "axlen[1]") / xblock + nlines = qp_geti (qp, "axlen[2]") / yblock + + gp = gopen ("stdgraph", NEW_FILE, STDGRAPH) + call gswind (gp, 1., real(ncols), 1., real(nlines)) + call gsetr (gp, G_ASPECT, 1.0) + + call clgstr ("eventlist", Memc[evlist], SZ_EXPR) + io = qpio_open (qp, Memc[evlist], READ_ONLY) + + if (Memc[evlist] == EOS) + call glabax (gp, "events", "X", "Y") + else + call glabax (gp, Memc[evlist], "X", "Y") + + # Scan the event list. + while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) { + do i = 1, nev { + ev = Memi[evl+i-1] + Memr[xv+i-1] = EV_X(ev) / xblock + 1.0 + Memr[yv+i-1] = EV_Y(ev) / yblock + 1.0 + } + call gpmark (gp, Memr[xv], Memr[yv], nev, GM_POINT, 0.0, 0.0) + call gflush (gp) + } + + call qpio_close (io) + call gclose (gp) + + call qp_close (qp) + call sfree (sp) +end + + +# SUM -- Sum the counts in an image section. + +procedure t_sum() + +double sum +char image[SZ_LINE] +int ncols, nlines, i +pointer im, immap(), imgl2i() +real asumi() + +begin + call clgstr ("image", image, SZ_LINE) + im = immap (image, READ_ONLY, 0) + + ncols = IM_LEN(im,1) + nlines = IM_LEN(im,2) + + call printf ("ncols=%d, nlines=%d, pixtype=%d\n") + call pargi (ncols) + call pargi (nlines) + call pargi (IM_PIXTYPE(im)) + call flush (STDOUT) + + sum = 0 + do i = 1, nlines + sum = sum + asumi (Memi[imgl2i(im,i)], ncols) + + call printf ("total pixels = %d, counts = %14.0f\n") + call pargi (ncols * nlines) + call pargd (sum) + + call imunmap (im) +end + + +# SETWCS -- Store a wcs in a QPOE file. + +procedure t_setwcs() + +pointer qp, mw +char text[SZ_LINE] +pointer qp_open, mw_open + +begin + call clgstr ("poefile", text, SZ_LINE) + qp = qp_open (text, READ_WRITE, 0) + + mw = mw_open (NULL, 2) + call qp_savewcs (qp, mw) + + call mw_close (mw) + call qp_close (qp) +end + + +# SETFILT -- Set the default filter in a QPOE file. + +procedure t_setfilt() + +pointer qp +char poefile[SZ_FNAME] +char filter[SZ_LINE] +pointer qp_open() + +begin + call clgstr ("poefile", poefile, SZ_FNAME) + qp = qp_open (poefile, READ_WRITE, 0) + + call clgstr ("deffilt", filter, SZ_LINE) + call qp_astr (qp, "deffilt", filter, "default filter") + + call qp_close (qp) +end + + +# SETMASK -- Set the default mask in a QPOE file. + +procedure t_setmask() + +pointer qp +char poefile[SZ_FNAME] +char mask[SZ_LINE] +pointer qp_open() + +begin + call clgstr ("poefile", poefile, SZ_FNAME) + qp = qp_open (poefile, READ_WRITE, 0) + + call clgstr ("defmask", mask, SZ_LINE) + call qp_astr (qp, "defmask", mask, "default mask") + + call qp_close (qp) +end + + +# MERGEI -- Test the merge range list routine (integer version). +# The lists may be specified either as strings, or as @file-name. + +procedure t_mergei() + +int p1, p2 +char list1[SZ_LINE], list2[SZ_LINE] +pointer sp, rl1, rl2, op, xs, xe, ys, ye, os, oe +int fd, ch, xlen, ylen, olen, nx, ny, nout, i +int open(), getci(), qpex_parsei(), qp_rlmergei() + +begin + call smark (sp) + call salloc (rl1, SZ_RLBUF, TY_CHAR) + call salloc (rl2, SZ_RLBUF, TY_CHAR) + + # Get the first range list. + call clgstr ("list1", list1, SZ_LINE) + if (list1[1] == '@') { + fd = open (list1[2], READ_ONLY, TEXT_FILE) + op = rl1 + while (getci (fd, ch) != EOF) { + if (ch == '\n') + ch = ' ' + Memc[op] = ch + op = op + 1 + } + Memc[op] = EOS + } else + call strcpy (list1, Memc[rl1], SZ_RLBUF) + + # Get the second range list. + call clgstr ("list2", list2, SZ_LINE) + if (list2[1] == '@') { + fd = open (list2[2], READ_ONLY, TEXT_FILE) + op = rl2 + while (getci (fd, ch) != EOF) { + if (ch == '\n') + ch = ' ' + Memc[op] = ch + op = op + 1 + } + Memc[op] = EOS + } else + call strcpy (list2, Memc[rl2], SZ_RLBUF) + + # Parse the lists. + xlen = 100 + call malloc (xs, xlen, TY_INT) + call malloc (xe, xlen, TY_INT) + nx = qpex_parsei (Memc[rl1], xs, xe, xlen) + + ylen = 100 + call malloc (ys, ylen, TY_INT) + call malloc (ye, ylen, TY_INT) + ny = qpex_parsei (Memc[rl2], ys, ye, ylen) + + # Merge the lists. + olen = 100 + call malloc (os, olen, TY_INT) + call malloc (oe, olen, TY_INT) + nout = qp_rlmergei (os,oe,olen, + Memi[xs],Memi[xe],nx, Memi[ys],Memi[ye],ny) + + # Print results: + call printf ("---- list 1 -----\n") + do i = 1, nx { + p1 = Memi[xs+i-1] + p2 = Memi[xe+i-1] + call printf ("%8d %8s : %8s\n") + call pargi (i) + if (IS_LEFTI(p1)) + call pargstr ("left") + else + call pargi (p1) + if (IS_RIGHTI(p2)) + call pargstr ("right") + else + call pargi (p2) + } + + call printf ("---- list 2 -----\n") + do i = 1, ny { + p1 = Memi[ys+i-1] + p2 = Memi[ye+i-1] + call printf ("%8d %8s : %8s\n") + call pargi (i) + if (IS_LEFTI(p1)) + call pargstr ("left") + else + call pargi (p1) + if (IS_RIGHTI(p2)) + call pargstr ("right") + else + call pargi (p2) + } + + call printf ("---- merged -----\n") + do i = 1, nout { + p1 = Memi[os+i-1] + p2 = Memi[oe+i-1] + call printf ("%8d %8s : %8s\n") + call pargi (i) + if (IS_LEFTI(p1)) + call pargstr ("left") + else + call pargi (p1) + if (IS_RIGHTI(p2)) + call pargstr ("right") + else + call pargi (p2) + } + + # Free list storage. + call mfree (xs, TY_INT); call mfree (xe, TY_INT) + call mfree (ys, TY_INT); call mfree (ye, TY_INT) + call mfree (os, TY_INT); call mfree (oe, TY_INT) + + call sfree (sp) +end + + +# CLEAR -- Clear the terminal screen. + +procedure t_clear() + +pointer tty +pointer ttyodes() +errchk ttyodes + +begin + # Clear the screen. + tty = ttyodes ("terminal") + call ttyclear (STDOUT, tty) + call ttycdes (tty) +end |