gd_entry(3)

gd_entry(3) β€” GETDATA, but make it make sense ✨

okay so bestie, pull up a chair πŸͺ‘ because we're about to talk about gd_entry, and it's lowkey a whole thing. it lives in the GETDATA corner of the manual, and it's the vibe check you run on a Dirfile field to find out what it actually is. let's get into it πŸ’–


🌸 NAME 🌸

gd_entry β€” retrieve a Dirfile field's metadata


✨ SYNOPSIS ✨

#include <getdata.h>

int gd_entry(DIRFILE *dirfile, const char *field_code, gd_entry_t
       *entry);

πŸŽ€ DESCRIPTION πŸŽ€

so the gd_entry() function goes and queries a dirfile(5) database β€” the one you point at with dirfile β€” and it pulls back the metadata for whatever field you named in field_code. cute, right? and here's a lil detail: if field_code has a valid representation suffix on it, that suffix just gets ignored. we don't care, we're not here for that πŸ’…

the dirfile argument has to point to a valid DIRFILE object that you already made with a call to gd_open(3). no fake ones, bestie, it will not be fooled.

the entry lands in the gd_entry_t structure that entry points at, and you have to allocate that yourself β€” it is not allocated for you. which members you actually get populated depends on the field type of the field you queried. there's a full rundown of this data type below πŸ‘‡

now, the string members that get filled in by this function (and depending on field type, that means the field member, elements of the in_fields and scalar arrays, and the LINTERP table member) are dynamically allocated by gd_entry(). only strings that the gd_entry_t provides for that particular field type get allocated β€” nothing extra, no surprises. by default these strings come from strdup(3), but you can switch that up by specifying a different memory manager via gd_alloc_funcs(3). and guess what β€” you're the one responsible for freeing those strings 🫢. there's a lil helper, gd_free_entry_strings(3), that does it for you if you don't feel like doing it by hand.

okay, important part, pay attention: if the entry's metadata has scalar field codes that can't be dereferenced, the associated numerical field parameter gets initialised to zero. and in that case? the GD_EN_CALC flag in the returned entry object is NOT set. noted πŸ“

and good news β€” the returned entry structure, strings and all their pointers included, can be freely modified by you. it's yours now, do what you want with it πŸ’–


πŸ”₯ RETURN VALUE πŸ”₯

when it works, gd_entry() returns zero and writes the field metadata into the gd_entry_t buffer you supplied. slay.

on error though? the supplied gd_entry_t buffer is not modified (iconic of it, honestly), and gd_entry() returns a negative-valued error code. possible error codes are:

  • GD_E_ALLOC β€” the library couldn't allocate memory 😭
  • GD_E_BAD_CODE β€” the field you named in field_code was not found in the database. it's giving "who?"
  • GD_E_BAD_DIRFILE β€” the dirfile you handed over was invalid

the error code also gets stashed in the DIRFILE object, so you can grab it after this function returns by calling gd_error(3). and if you want a descriptive error string β€” like, actual words β€” call gd_error_string(3). iykyk πŸ’œ


πŸ‘‘ THE ENTRY TYPE πŸ‘‘

so which members exist in a gd_entry_t depends entirely on the field type you're describing. but every gd_entry_t object is guaranteed to have at least these:

typedef struct {
  ...

  const char  *field;          /* field code */
  gd_entype_t  field_type;     /* field type */
  int          fragment_index; /* format fragment index */
  unsigned     flags;          /* entry flags */

  ...
} gd_entry_t;

let's break those down, bestie:

the field member is the field code of the entry β€” aka its string name. and if your gd_entry(3) call succeeded, this will be the field name you specified as part of the field_code argument. cute.

the field_type member tells you the field type of the entry. it's an integer type equal to one of these symbols:

GD_BIT_ENTRY, GD_CARRAY_ENTRY, GD_CONST_ENTRY, GD_DIVIDE_ENTRY,
GD_INDEX_ENTRY, GD_LINCOM_ENTRY, GD_LINTERP_ENTRY,
GD_MPLEX_ENTRY, GD_MULTIPLY_ENTRY, GD_PHASE_ENTRY,
GD_POLYNOM_ENTRY, GD_RAW_ENTRY, GD_RECIP_ENTRY, GD_SBIT_ENTRY,
GD_STRING_ENTRY, GD_WINDOW_ENTRY.

quick note: GD_INDEX_ENTRY is a special field type used only for the implicit INDEX field. the other entry types get explained in detail over in dirfile-format(5).

the fragment_index member tells you which format specification fragment this field is defined in. it's an integer index into the Dirfile's list of parsed format specification fragments. want the actual filename for a given fragment_index? hit up gd_fragmentname(3). and if this value is zero, that means the field lives in the primary fragment β€” the file called format in the root dirfile directory (peep dirfile(5)).

the flags member is a bitwise-or'd collection of these entry flags:

  • GD_EN_CALC β€” this bit is set only when the non-literal scalar parameter field codes specified in the scalar member have been resolved, and the corresponding numerical parameter has been initialised with these data. when one or more field code doesn't exist, or is invalid (ie. when gd_validate(3) would fail on the specified field_code with the error GD_E_BAD_SCALAR), then the unresolved numerical parameters get initialised to zero, and this flag is not set. and heads up: if the requested field doesn't allow non-literal scalar parameters (CARRAY, CONST, DIVIDE, INDEX, LINTERP, MULTIPLY, STRING), the value of this bit is unspecified πŸ™ˆ
  • GD_EN_COMPSCAL β€” for fields that permit complex valued parameters (LINCOM, POLYNOM, RECIP), this bit is set only when at least one parameter is complex valued. for other field types, the value of this bit is unspecified
  • GD_EN_HIDDEN β€” this bit is set only when the field has been hidden by the /HIDDEN directive (see gd_hidden(3))

the remaining fields in the gd_entry_t structure depend on the value of field_type. so please, bestie, check field_type before you go reaching for the remaining members πŸ‘. members for different field types may be stored in the same physical location in core. so if you try to access a member that isn't declared for the appropriate field type, the results are unspecified. dangerous behavior, we don't do that πŸ’€


🌈 Scalar Parameter Members 🌈

a gd_entry_t describing any field type that permits non-literal scalar field parameters (BIT, LINCOM, MPLEX, PHASE, POLYNOM, RAW, RECIP, SBIT, or WINDOW) will also provide:

typedef struct {
  ...

  const char *scalar[GD_MAX_POLY_ORD + 1];   /* param. fields */
  int         scalar_ind[GD_MAX_POLY_ORD + 1];
                                            /* CARRAY indices */

  ...
} gd_entry_t;

only certain elements of these arrays get initialised. here's the tea:

  • for BIT and SBIT fields, the first element corresponds to bitnum and the second to numbits. the remainder are uninitialised.
  • for LINCOM fields, the first GD_MAX_LINCOM elements correspond to the slopes (cm) and the next GD_MAX_LINCOM elements correspond to the offsets (cb). only the first n_fields elements of these two sets are initialised. and notably β€” this one's sneaky β€” for n_fields < GD_MAX_LINCOM, there will be uninitialised elements in the middle of these arrays, between the element corresponding to cm[n_fields - 1] and the element corresponding to cb[0]. yeah. watch for that one 😭
  • for MPLEX fields, the first element corresponds to count_val and the second to period. the remainder are uninitialised.
  • for PHASE fields, the first element corresponds to shift. the remainder are uninitialised.
  • for POLYNOM fields, these arrays correspond with the co-efficients ca. only the first poly_ord + 1 elements are initialised.
  • for RAW fields, the first element corresponds to spf. the remainder are uninitialised.
  • for RECIP fields, the first element corresponds to cdividend. the remainder are uninitialised.
  • for WINDOW fields, the first element corresponds to threshold. the remainder are uninitialised.

the scalar parameters are NULL if a literal parameter was used, or else a field code specifying the scalar parameters.

and if an element of scalar specifies a CARRAY field, the corresponding scalar_ind will indicate the element of the CARRAY used. for CONST fields, scalar_ind will be -1. ⭐


πŸ¦‹ BIT and SBIT Members πŸ¦‹

a gd_entry_t describing a BIT or SBIT entry will also provide:

typedef struct {
  ...

  const char *in_fields[1];     /* input field code */
  int         bitnum;           /* first bit */
  int         numbits;          /* bit length */

  ...
} gd_entry_t;

the in_fields member is an array of length one containing the input field code.

the bitnum member indicates the number of the first bit (counted from zero) extracted from the input. if this value was specified as a scalar field code, then this will be the numerical value of that field, and scalar[0] will contain the field code itself β€” otherwise scalar[0] will be NULL.

the numbits member indicates the number of bits which are extracted from the input. if this value was specified as a scalar field code, then this will be the numerical value of that field, and scalar[1] will contain the field code itself β€” otherwise scalar[1] will be NULL.


πŸ“ CARRAY Members πŸ“

a gd_entry_t describing a CARRAY entry will also provide:

typedef struct {
  ...

  gd_type_t  const_type; /* data type in format specification */
  size_t     array_len;  /* length of array data */

  ...
} gd_entry_t;

the const_type member indicates the data type of the constant value stored in the format file metadata. see gd_getdata(3) for a list of valid values that a variable of type gd_type_t may take.

the array_len member gives the number of elements in the array.


🧚 CONST Members 🧚

a gd_entry_t describing a CONST entry will also provide:

typedef struct {
  ...

  gd_type_t  const_type; /* data type in format specification */

  ...
} gd_entry_t;

the const_type member indicates the data type of the constant value stored in the format file metadata. see gd_getdata(3) for a list of valid values that a variable of type gd_type_t may take.


πŸ’œ DIVIDE, INDIR, MULTIPLY, and SINDIR Members πŸ’œ

a gd_entry_t describing a DIVIDE, INDIR, MULTIPLY, or SINDIR entry will also provide:

typedef struct {
  ...

  const char *in_fields[2];     /* input field codes */

  ...
} gd_entry_t;

the in_fields member is an array of length two containing the input field codes.


πŸ’– INDEX Members πŸ’–

a gd_entry_t describing an INDEX entry β€” which is used only for the implicit INDEX field β€” provides no additional data. that's it, that's the whole thing. minimal. iconic. πŸ’…


πŸŽ€ LINCOM Members πŸŽ€

a gd_entry_t describing a LINCOM entry will also provide:

typedef struct {
  ...

so the LINCOM struct keeps going like this:

                int            n_fields;                 /* # of inputs */
                const char    *in_fields[GD_MAX_LINCOM]; /* input fields(s) */
                double complex cm[GD_MAX_LINCOM];        /* scale factor(s) */
                double         m[GD_MAX_LINCOM];         /* scale factor(s) */
                double complex cb[GD_MAX_LINCOM];        /* offset terms(s) */
                double         b[GD_MAX_LINCOM];         /* offset terms(s) */

                ...
              } gd_entry_t;

n_fields is literally the number of input fields, bestie. It's between one and GD_MAX_LINCOM inclusive. And GD_MAX_LINCOM? That's defined in getdata.h as the max number of input fields a LINCOM can handle. πŸ’…

in_fields is an array of length GD_MAX_LINCOM holding the input field code(s). Only the first n_fields elements are actually initialised β€” the rest are just uninitialised data, no cap.

cm and cb are arrays of the scale factor(s) and offset term(s) for the LINCOM. Only the first n_fields elements of these arrays contain meaningful data. If any of these values were specified as a scalar field code, this will be the numerical value of that field. The field code corresponding to cm[i] will be stored in scalar[i], and the field code associated with cb[i] will be stored in scalar[i + GD_MAX_LINCOM]. Otherwise the corresponding scalar member will be NULL. Peep the NOTES below for changes to the declaration of cm and cb when using the C89 GetData API.

The elements of m and b are the real parts of the corresponding elements of cm and cb.


🌸 LINTERP Members 🌸

A gd_entry_t describing a LINTERP entry will also provide:

              typedef struct {
                ...

                const char *table             /* linterp table filename */
                const char *in_fields[1];     /* input field code */

                ...
              } gd_entry_t;

table is the pathname to the look up table on disk. This is the path as it appears in the format specification. It may be a path relative to the fragment directory. For a canonicalised, absolute version of this path, see gd_linterp_tablename(3).

in_fields is an array of length one containing the input field code.


πŸ”₯ MPLEX Members πŸ”₯

A gd_entry_t describing a MPLEX entry will also provide:

              typedef struct {
                ...

                const char *in_fields[2];   /* input field codes */
                int         count_val;      /* value of the multiplex index */
                int         period;         /* samples between successive
                                                                    indices */

                ...
              } gd_entry_t;

in_fields contains the field codes of the input field (element 0) and the multiplex index field (element 1).

count_val is the value of the multiplex index field when the output field is stored in the input field.

period is the number of samples between successive occurrences of count_val in the index vector, or zero if this is not known or constant. This is only used to determine how far to look back for a starting value for the output field; see gd_mplex_lookback(3).


πŸ’œ PHASE Members πŸ’œ

A gd_entry_t describing a PHASE entry will also provide:

              typedef struct {
                ...

                const char *in_fields[1];     /* input field code */
                gd_int64_t  shift;            /* phase shift */

                ...
              } gd_entry_t;

in_fields is an array of length one containing the input field code.

shift indicates the shift in samples. The gd_int64_t type is a 64-bit signed integer type. A positive value indicates a shift forward in time (towards larger frame numbers). If this value was specified as a scalar field code, this will be the numerical value of that field, and scalar[0] will contain the field code itself, otherwise scalar[0] will be NULL.


🌈 POLYNOM Members 🌈

A gd_entry_t describing a POLYNOM entry will also provide:

              typedef struct {
                ...

                int            poly_ord;                /* polynomial order */
                const char    *in_fields[1];            /* input field(s) */
                double complex ca[GD_MAX_POLY_ORD + 1]; /* co-efficients(s) */
                double         a[GD_MAX_POLY_ORD + 1];  /* co-efficients(s) */

                ...
              } gd_entry_t;

poly_ord indicates the order of the polynomial. It will be between one and GD_MAX_POLY_ORD inclusive. GD_MAX_POLY_ORD is defined in getdata.h as the maximum order of polynomial permitted by a POLYNOM.

in_fields is an array of length one containing the input field code.

ca is an array of the co-efficient(s) for the POLYNOM. Only the first poly_ord + 1 elements of this array contain meaningful data. If any of these values were specified as a scalar field code, this will be the numerical value of that field. The field code corresponding to ca[i] will be stored in scalar[i]. Otherwise the corresponding scalar member will be NULL. Peep the NOTES below for changes to the declaration of ca when using the C89 GetData API.

The elements of a are the real parts of the corresponding elements of ca.


πŸ“ RAW Members πŸ“

A gd_entry_t describing a RAW entry will also provide:

              typedef struct {
                ...

                unsigned int  spf;          /* samples per frame on disk */
                gd_type_t     data_type;    /* data type on disk */

                ...
              } gd_entry_t;

spf contains the samples per frame of the binary data on disk. If this value was specified as a scalar field code, this will be the numerical value of that field, and scalar[0] will contain the field code itself, otherwise scalar[0] will be NULL.

data_type indicates the data type of the binary data on disk. See gd_getdata(3) for a list of valid values that a variable of type gd_type_t may take.


πŸŽ€ RECIP Members πŸŽ€

A gd_entry_t describing a RECIP entry will also provide:

              typedef struct {
                ...

                const char    *in_fields[1];         /* input field code */
                double complex cdividend;            /* scalar dividend */
                double         dividend;             /* scalar dividend */

                ...
              } gd_entry_t;

in_fields is an array of length one containing the input field code.

cdividend provides the constant dividend of the computed division. If this value was specified as a scalar field code, this will be the numerical value of that field, and scalar[0] will contain the field code itself, otherwise scalar[0] will be NULL. The dividend member contains the real part of cdividend.


πŸ¦‹ STRING Members πŸ¦‹

A gd_entry_t describing a STRING entry provides no additional data.


πŸ‘‘ WINDOW Members πŸ‘‘

A gd_entry_t describing a WINDOW entry will also provide:

              typedef struct {
                ...

                const char    *in_fields[2];  /* input field codes */
                gd_windop_t   windop;         /* comparison operator */
                gd_triplet_t  threshold;      /* the value compared against */

                ...
              } gd_entry_t;

in_fields contains the field codes of the input field (element 0) and the check field (element 1).

windop equals one of the following symbols, indicating the particular comparison performed on the check field:

  • GD_WINDOP_EQ β€” data are extracted when the check field equals threshold;
  • GD_WINDOP_GE β€” data are extracted when the check field is greater than or equal to threshold;
  • GD_WINDOP_GT β€” data are extracted when the check field is strictly greater than threshold;
  • GD_WINDOP_LE β€” data are extracted when the check field is less than or equal to threshold;
  • GD_WINDOP_LT β€” data are extracted when the check field is strictly less than threshold;
  • GD_WINDOP_NE β€” data are extracted when the check field is not equal to threshold;
  • GD_WINDOP_SET β€” data are extracted when at least one bit in threshold is also set in the check field;
  • GD_WINDOP_CLR β€” data are extracted when at least one bit in threshold is not set in the check field.

threshold is the value against which the check field is compared. The gd_triplet_t type is defined as:

              typedef union {
                gd_int64_t i;
                gd_uint64_t u;
                double r;
              } gd_triplet_t;

The particular element of the union to use depends on the value of windop:

  • For GD_WINDOP_EQ and GD_WINDOP_NE, the signed integer element, threshold.i, is set;
  • For GD_WINDOP_SET and GD_WINDOP_CLR, the unsigned integer element, threshold.u, is set;
  • For all other values of windop, the floating point element, threshold.r, is set.

πŸ“ NOTES πŸ“

okay so bestie, listen. there's a whole alternate universe of this API and it's called the C89 GetData API. you get there by defining GD_C89_API before you include getdata.h, and when you do that, several of the entry parameters change their data types and their names. iconic of them honestly. πŸ’…

here's the correspondence table between the C99 members and the C89 members:

  • int bitnum ↔ int u.bit.bitnum
  • int numbits ↔ int u.bit.numbits
  • int n_fields ↔ int u.lincom.n_fields
  • double complex cm[] ↔ double u.lincom.cm[][2]
  • double m[] ↔ double u.lincom.m[]
  • double complex cb[] ↔ double u.lincom.cb[][2]
  • double b[] ↔ double u.lincom.b[]
  • const char* table ↔ const char* u.linterp.table
  • int count_val ↔ int u.mplex.count_val
  • int period ↔ int u.mplex.period
  • gd_int64_t shift ↔ gd_int64_t u.phase.shift
  • int poly_ord ↔ int u.polynom.poly_ord
  • double complex ca[] ↔ double u.polynom.ca[][2]
  • double a[] ↔ double u.polynom.a[]
  • unsigned int spf ↔ unsigned int u.raw.spf
  • gd_type_t data_type ↔ gd_type_t u.raw.data_type
  • double complex cdividend ↔ double u.recip.cdividend[2]
  • double dividend ↔ double u.recip.dividend
  • gd_type_t const_type ↔ gd_type_t u.scalar.const_type
  • size_t array_len ↔ size_t u.scalar.array_len
  • gd_windop_t windop ↔ gd_windop_t u.window.windop
  • gd_triplet_t threshold ↔ gd_triplet_t u.window.threshold

and for the complex valued data in the C89 API, just so you're not out here guessing: the first element of that two-element array is the real part of the complex number, and the second element is the imaginary part. periodt. 🫢


πŸ“œ HISTORY πŸ“œ

so the lore goes like this:

  • get_entry() first showed up in GetData-0.3.0.
  • then in GetData-0.7.0, this function got a glow-up and was renamed to gd_entry().
  • and in GetData-0.10.0, the error return from these functions changed from -1 to a negative-valued error code.

🧬 Changes to the gd_entry_t structure

field-type specific members got added to the structure as support for those field types got introduced to the library, one era at a time:

  • BIT, LINCOM, LINTERP, MULTIPLY, PHASE, and RAW were supported in GetData-0.3.0 (Dirfile Standards Version 5).
  • CONST and STRING entries were introduced in GetData-0.4.0 (Dirfile Standards Version 6); this is also the first version that treats INDEX as a normal field. in earlier versions, trying to retrieve the metadata for the INDEX field would just fail. rip. πŸ’€
  • POLYNOM and SBIT entries were introduced in GetData-0.6.0 (Dirfile Standards Version 7).
  • CARRAY, DIVIDE, and RECIP entries were introduced in GetData-0.7.0 (Dirfile Standards Version 8).
  • MPLEX and WINDOW entries were introduced in GetData-0.8.0 (Dirfile Standards Version 9).
  • INDIR, SARRAY, and SINDIR entries were introduced in GetData-0.10.0 (Dirfile Standards Version 10).

and a few more receipts:

  • the scalar member appeared in GetData-0.6.0. this release also introduced the complex-valued scalar members (cm, cb, &c.)
  • the scalar_ind member appeared in GetData-0.7.0. this was also the first release with a working ANSI C (C89) conforming alternate definition. ✨
  • before Getdata-0.8.4, the period member for MPLEX data was named count_max.
  • before GetData-0.9.0, the flags member is missing. in it's place was:
int comp_scal;     /* Scalar parameters are complex-valued */

which was non-zero to indicate complex-valued parameters, which is now indicated by the GD_EN_COMPSCAL flag. glow-up confirmed. πŸ‘‘


πŸ‘€ SEE ALSO πŸ‘€

go read these next, bestie, they're all part of the ecosystem:


that's the whole page, you made it, i'm so proud of you πŸ₯ΉπŸ’– this one's Version 0.10.0, dated 25 December 2016, and it's been serving gd_entry(3) realness ever since. go forth and read your dirfiles. πŸŒˆπŸ¦‹πŸ’œ