MEF 3 Specification
MEF 3 Specification
(MEF3)
Feature Overview:
Feature Characteristics
• Any time series data can be encoded (e.g. transforms of original data)
• AES 128-bit
• HIPAA compliant
• 32-bit CRC checksums for detection of file, individual record, & time series
block corruption
• Time Series Channels:
Redundancy &
Damage • Block independence limits extent of data loss if damage occurs
mitigation • Block alignment facilitates file recovery
• µUTC time provides globally accurate date & time of day to microsecond
Time resolution
• µUTC time is easily converted to UTC time for use with standard Unix / Posix
time functions
MEF Strings
• All strings related to naming and descriptive data use UTF-8 encoding to allow for
international character sets.
• UTF-8 encoding:
• variable length characters
• up to 4 bytes per character
• not endian-sensitive
• strings are null-terminated
• Unused bytes in MEF string fields are set to zero to promote reproducibility of CRC
values.
UTF-8 passwords
• AES-128 requires a 16 byte key. Therefore multibyte UTF-8 password characters
are used internally in MEF by taking the last (most unique) byte in each character of
the UTF-8 encoding.
• The password length limit is 15 (UTF-8) characters because MEF passwords are
required to be null terminated strings.
0 No encryption
1 Level 1 encrypted
2 Level 2 encrypted
-128 No entry
Universal Header
• Each file in the MEF structure begins with a universal header
• The only current exception is video data files whose format is determined by their
specific video format (e.g. MPEG).
• The universal header is not encrypted.
• Design concepts:
• Contain the minimum information required to read a file in the absence of any
other files (e.g. indices or metadata). Appropriate interpretation of the data may
still require metadata and passwords. In some file types universal header
information may be duplicated in the metadata for convenience.
• Contain the minimum information to uniquely identify a file and it’s place in a MEF
hierarchy.
• Contain the minimum information required to detect file corruption.
• Contain no potentially subject identifying information.
Universal Header:
Field Offset Bytes Type Contents
• 0 ==> big-endian
• -1 indicates no entry
Segment Number 48 4 si4
• -2 indicates channel level
• Number of bytes in a
tmet
Metadata Files 1 metadata file (a constant)
vmet
• -1 indicates no entry
Section 1
Metadata
Section 2 See channel type specific As specified in
2560 10752
Channel Type tables below Section 1
Specific Fields
• µUTC of Daylight
Saving Time start, if
occurred during
recording
As specified in
DST Start Time 13320 8 si8
• 0 indicates DST did not Section 1
begin during recording
• 0x8000000000000000
indicates no entry
Field Offset Bytes Type Contents Encryption
• µUTC of Daylight
Saving Time end, if
occurred during
recording
As specified in
DST End Time 13328 8 si8
• 0 indicates DST did not Section 1
end during recording
• 0x8000000000000000
indicates no entry
• subject ID
As specified in
Subject ID 13596 128 utf8[31] • Zero-length string Section 1
indicates no entry
• Typically: Originating
Recording Institution, City, Country As specified in
13724 512 utf8[127]
Location • Zero-length string Section 1
indicates no entry
• Description of recording
channel
• Description of recording
session
• Pecording duration in
microseconds
Recording As specified in
6656 8 si8 • -1 indicates no entry
Duration Section 1
• Present in all section 2
metadata types
• Description of recording
Reference reference channel As specified in
6664 2048 utf8[511]
Description • Zero-length string Section 1
indicates no entry
• Maximum bytes,
including header & pad
Maximum bytes, in any RED block As specified in
8936 8 si8
Block Bytes in the file Section 1
• -1 indicates no entry
• Maximum number of
samples in a RED block
• 0xFFFFFFFF indicates
Maximum As specified in
8944 4 ui4 no entry
Block Samples Section 1
• Duplicated (as an si8) in
Universal Header of
Time Series Data Files
• Maximum bytes
required for the
Maximum difference data in the As specified in
Difference 8948 4 ui4 compressed blocks Section 1
Bytes
• 0xFFFFFFFF indicates
no entry
Field Offset Bytes Type Contents Encryption
• Microseconds between
RED blocks As specified in
Block Interval 8952 8 si8
• -1 indicates no entry, or Section 1
that the intervals vary
• Number of
discontinuities in the
segment
Number of As specified in
8960 8 si8
Discontinuities • First sample is a Section 1
discontinuity
• -1 indicates no entry
• Maximum number of
Maximum contiguous RED blocks
between discontinuities As specified in
Contiguous 8968 8 si8
in the segment Section 1
Blocks
• -1 indicates no entry
• Maximum number of
contiguous compressed
bytes between
Maximum discontinuities in the As specified in
Contiguous 8976 8 si8 segment (including Section 1
Block Bytes block headers and pad
bytes)
• -1 indicates no entry
• Maximum number of
Maximum contiguous samples As specified in
Contiguous 8984 8 si8 between discontinuities Section 1
Samples
• -1 indicates no entry
• Description of recording
session
• recording duration in
microseconds
Recording As specified
6656 8 si8 • -1 indicates no entry
Duration in Section 1
• Present in all section 2
types
• e.g. “MPEG-4”
As specified
Video Format 6704 128 utf8[31] • Zero-length string in Section 1
indicates no entry
…
Record Header Format:
Field Offset Bytes Type Contents Encryption
• 0 indicates no entry
…
Record Index Format:
Field Offset Bytes Type Contents
Time Series
512 32 See “Time Series Index Format” description
Index…
...
Time Series Index Format:
Field Offset Bytes Type Contents
• µUTC time
RED Block
Protected 45 3 From RED block header
Region
RED Block
Discretionary 48 8 From RED block header
Region
Universal
0 512 See “Universal Header” description
Header
...
Video Index Format:
Field Offset Bytes Type Contents
Universal
0 512 See “Universal Header” description
Header
...
RED Blocks
• Data are stored in compressed independent blocks
• Raw data are differenced. Differences are encoded in a single signed byte. If there is
overflow, i.e > +127 or < -127, then a keysample is introduced flagged by the
reserved value -128. The 4 bytes following the keysample flag contain the full
undifferenced value of the (second) data point generating the overflow difference, as
an si4.
• The differenced data are statistically modeled, the model is stored in the RED block
header.
• Range encoding is used to compress the differences, using the statistical model.
• Blocks are required to be 8-byte boundary aligned, and are terminally padded to an
8-byte boundary with the value 0x7E (tilde, “~”) as necessary. Pad bytes are
included in the block bytes value, and in the block CRC.
• In compression, if the RED_PROCESSING_DIRECTIVE detrend_data is set, each
sample will be dtretended prior to scaling and compressing. The slope and intercept
will be stored in the block header. This is a lossless operation, but has more utility in
lossy compression.
• In compression, if the value of the scale_factor is greater than 1.0, the (possibly
offset) values will be divided by this value and rounded, prior to differencing. This is
a lossy operation.
• In decompression, if the value of the scale_factor is greater than 1.0, the values of
the samples will be multiplied by this value and rounded after un-differencing.
• In decompression, if the block offset is non-zero, this value will be added to each of
the samples after un-differencing and possibly scaling.
RED Block Format:
Field Offset Bytes Type Contents
Discretionary
8 8 discretionary end-user use
Region
• µUTC time.
Start Time 40 8 si8 • If recording time offset is used for the
session it is applied here also.
• 0 indicates no discontinuity
Bit 1 Level 1 Encrypted • The encryption level desired is set by the “encryption” field in the
Block Bit RED_PROCESSING_DIRECTIVES.
• This bit is mutually exclusive with “Level 2 Encrypted Block
Bit” (bit 2)
Bits 3 -
reserved for future use
7
Meflib API
The meflib API functions and headers are contained in the files “meflib.c” & “meflib.h”.
While this is open source, the general idea is that user-defined code not be added to
these files. User defined records are defined and coded in “mefrec.c” and “mefrec.h”.
The functions required for adding a new record type are described in “MEF 3 Records
Specification”
/************************************************************************************/
/******************************** Elemental Typedefs ********************************/
/************************************************************************************/
These typedefs are used throughout the library to facilitate compilation on systems with
different word sizes.
The first character indicates signedness, “s” for signed, “u” for unsigned.
The second character indicates format: “i” for integer type, “f” for floating point type.
/************************************************************************************/
/************************************ MEF Booleans **********************************/
/************************************************************************************/
#define MEF_TRUE 1
#define MEF_UNKNOWN 0
#define MEF_FALSE -1
A balanced ternary schema including true, unknown, & false states. This is used
throughout the library, and is typically represented in an si1 type.
/************************************************************************************/
/************************************ MEF Globals *********************************/
/************************************************************************************/
// Structures
typedef struct {
// time constants
si8 recording_time_offset;
ui4 recording_time_offset_mode;
si4 GMT_offset;
si8 DST_start_time;
si8 DST_end_time;
// alignment fields
si4 universal_header_aligned;
si4 metadata_section_1_aligned;
si4 time_series_metadata_section_2_aligned;
si4 video_metadata_section_2_aligned;
si4 metadata_section_3_aligned;
si4 all_metadata_structures_aligned;
si4 time_series_indices_aligned;
si4 video_indices_aligned;
si4 RED_block_header_aligned;
si4 record_header_aligned;
si4 record_indices_aligned;
si4 all_record_structures_aligned;
si4 all_structures_aligned;
// RED
sf8 *RED_normal_CDF_table;
// CRC
ui4 *CRC_table;
ui4 CRC_mode;
// AES tables
si4 *AES_sbox_table;
si4 *AES_rcon_table;
si4 *AES_rsbox_table;
// SHA256 tables
ui4 *SHA256_h0_table;
ui4 *SHA256_k_table;
// UTF8 tables
ui4 *UTF8_offsets_from_UTF8_table;
si1 *UTF8_trailing_bytes_for_UTF8_table;
// miscellaneous
si4 verbose;
ui4 behavior_on_fail;
ui4 file_creation_umask;
} MEF_GLOBALS;
/ Global Defaults
#define MEF_GLOBALS_VERBOSE_DEFAULT MEF_FALSE
#define MEF_GLOBALS_RECORDING_TIME_OFFSET_DEFAULT 0
#define MEF_GLOBALS_RECORDING_TIME_OFFSET_MODE_DEFAULT (RTO_APPLY_ON_OUTPUT |
RTO_REMOVE_ON_INPUT)
#define MEF_GLOBALS_GMT_OFFSET_DEFAULT 0
#define MEF_GLOBALS_DST_START_TIME_DEFAULT UUTC_NO_ENTRY
#define MEF_GLOBALS_DST_END_TIME_DEFAULT UUTC_NO_ENTRY
#define MEF_GLOBALS_FILE_CREATION_UMASK_DEFAULT S_IWOTH // defined in <sys/stat.h>
#define MEF_GLOBALS_BEHAVIOR_ON_FAIL_DEFAULT EXIT_ON_FAIL
#define MEF_GLOBALS_CRC_MODE_DEFAULT CRC_CALCULATE_ON_OUTPUT
These values are used throughout the library in a thread-safe manner. They are
initialized to the application heap via the function initialize_MEF_globals(), which is in
turn called by initialize_meflib(). These two functions are described below.
/************************************************************************************/
/************************ Error Checking Standard Functions *************************/
/************************************************************************************/
// Constants
#define USE_GLOBAL_BEHAVIOR 0
#define RESTORE_BEHAVIOR 1
#define EXIT_ON_FAIL 2
#define RETURN_ON_FAIL 4
#define SUPPRESS_ERROR_OUTPUT 8
// Function Prototypes
void *e_calloc(size_t n_members, size_t size, const si1 *function, si4 line,
ui4 behavior_on_fail);
FILE *e_fopen(si1 *path, si1 *mode, const si1 *function, si4 line, ui4 behavior_on_fail);
size_t e_fread(void *ptr, size_t size, size_t n_members, FILE *stream, si1 *path,
const si1 *function, si4 line, ui4 behavior_on_fail);
si4 e_fseek(FILE *stream, size_t offset, si4 whence, si1 *path, const si1 *function, si4
line, ui4 behavior_on_fail);
long e_ftell(FILE *stream, const si1 *function, si4 line, ui4 behavior_on_fail);
size_t e_fwrite(void *ptr, size_t size, size_t n_members, FILE *stream, si1 *path,
const si1 *function, si4 line, ui4 behavior_on_fail);
void *e_malloc(size_t n_bytes, const si1 *function, si4 line, ui4 behavior_on_fail);
void *e_realloc(void *ptr, size_t n_bytes, const si1 *function, si4 line,
ui4 behavior_on_fail);
These functions are provided for convenience. They call their corresponding standard c
functions ( e.g. e_calloc() calls calloc() ), but have built in error messaging. The
behavior_on_fail parameter defines what the function does on failure.
example:
ui4 behavior;
si4 *data;
__FUNCTION__ and __LINE__ are compiler macros replaced with the function name
and line of the function in which they occur; these can contain any string and number,
however, for more complex failure tracking. Because of the way in which the behavior
parameter is defined, on failure, this call to e_calloc() will return NULL, as would
calloc(), and no error messages will be displayed. If USE_GLOBAL_BEHAVIOR is
passed into this parameter, the MEF_global value of behavior_on_fail will be used. This
is the most common usage in the library. At the time of this writing the default global
behavior_on_fail value is EXIT_ON_FAIL, which will produce error messages and then
exit the program.
/************************************************************************************/
/**************************** Alignment Checking Functions **************************/
/************************************************************************************/
// Prototypes
si4 check_all_alignments(const si1 *function, si4 line);
For example, on a 64 bit CPU structures are generally laid out on 8 byte boundaries. If
they are not inherently 8 byte aligned, the compiler will often pad the structure.
Explicitly padding the structure to create 8 byte alignment will alleviate this problem.
Likewise an 8 byte data type should fall on a natural 8 byte boundary within the
structure, if it does not the compiler may try to rearrange or pad the structure. In practice
designing a structure such that the compiler will leave it intact is usually quite easy. In
the case of alignment failure, the library would need to be updated to perform explicit
assignment.
The function check_all_alignments() calls all of the other alignment checking functions
and returns MEF_TRUE if all of those functions return MEF_TRUE. This function also
takes a function and line argument similar to the error checking functions. This function
is called from initialize_meflib(), and so need not be called explicitly if initialize_meflib()
is called.
If a buffer (the “bytes” field) is passed the function will not allocate any memory for the
testing. If NULL is passed in the “bytes” field the function will allocate memory for the
testing and then free it once the check is complete.
free(bytes);
return(return_value);
/************************************************************************************/
/**************************** General Purpose MEF Functions *************************/
/************************************************************************************/
As a group, these functions facilitate working with various aspects of the MEF format.
Each will be described separately below.
FUNCTION: all_zeros()
// Prototype
si1 all_zeros(ui1 *bytes, si4 field_length);
if (all_zeros(uh->level_1_password_validation_field, PASSWORD_VALIDATION_FIELD_BYTES) ==
MEF_TRUE)
printf("Level 1 Password Validation_Field: no entry\n");
FUNCTION: allocate_file_processing_struct()
// Prototype
FILE_PROCESSING_STRUCT *allocate_file_processing_struct(si8 raw_data_bytes, ui4 file_type_code,
FILE_PROCESSING_DIRECTIVES *directives, FILE_PROCESSING_STRUCT *proto_fps, si8
bytes_to_copy);
typedef struct {
si1 close_file;
si1 free_password_data; // when freeing FPS
si8 io_bytes; // bytes to read or write
ui4 lock_mode;
ui4 open_mode;
} FILE_PROCESSING_DIRECTIVES;
// Constants
The raw_data_bytes parameter specifies how much memory to allocate to the raw_data
array. This value is copied into the corresponding member of the new FPS.
FUNCTION: apply_recording_time_offset()
// Prototype
void apply_recording_time_offset(si8 *time);
The global recording time offset is applied to the passed µUTC time. If the value is
negative, it is presumed to already have had the recording time offset applied, and
nothing is done. The converse function is remove_recording_time_offset() described
below.
FUNCTION: check_password()
// Prototype
si4 check_password(si1 *password, const si1 *function, si4 line);
Checks that the password pointer is not NULL, and that the password length is less
than or equal to PASSWORD_BYTES. Returns 0 on success, 1 on failure. This function
does not validate the password against the password validation fields.
Process_password_data() does this. In fact, process_password_data() is the only
library function to call check_password().
FUNCTION: count_directories()
// Prototype
si4 count_directories(si1 *enclosing_directory, si1 *extension);
Returns the number of directories with the specified extension in an enclosing directory.
This function can be useful for knowing how many channels or segments currently exist
in a parallel processing situation.
FUNCTION: cpu_endianness()
// Constants
#define MEF_BIG_ENDIAN 0
#define MEF_LITTLE_ENDIAN 1
// Prototype
ui1 cpu_endianness();
Returns MEF_BIG_ENDIAN on big-endian machines and MEF_LITTLE_ENDIAN on
little-endian machines. The current library only supports little-endian MEF files, but the
specification supports both. If there is a future demand for big-endian MEF, the library
can be updated.
if (cpu_endianness() != MEF_LITTLE_ENDIAN)
fprintf(stderr, "Error: Library only coded for little-endian machines currently\n”);
FUNCTION: decrypt_metadata()
// Prototype
si4 decrypt_metadata(FILE_PROCESSING_STRUCT *fps);
Decrypts sections 2 and 3 of metadata file (passed in fps) if they are currently encrypted
and if access level is sufficient. It marks decrypted sections as decrypted (negative of
encryption level) in section 1 of the metadata.
FUNCTION: decrypt_records()
// Prototype
si4 decrypt_records(FILE_PROCESSING_STRUCT *fps);
// Constant
#define UNKNOWN_NUMBER_OF_ENTRIES -1
Decrypts records if they are currently encrypted and the access level is sufficient as
specified in the record header. Marks decrypted records as decrypted (negative of
encryption level) in record header. If the number of records is known (stored in the
universal header number_of_entries field), this value is used, if that is set to
UNKNOWN_NUMBER_OF_ENTRIES the function will work, as long as the
FILE_PROCESSING_STRUCT’s raw_data_bytes field reflects an integral number of
records.
The function also applies or removes the recording time offset to the times in the record
headers according to the value of the the MEF_global recording_time_offset_mode.
// Prototype
si4 encrypt_metadata(FILE_PROCESSING_STRUCT *fps);
Encrypts sections 2 and 3 of metadata file (passed in fps), if they are currently
decrypted, to the encryption level specified in section 1 of the metadata. It marks
encrypted sections as encrypted (positive of encryption level) in section 1 of the
metadata.
FUNCTION: encrypt_records()
// Constant
#define UNKNOWN_NUMBER_OF_ENTRIES -1
// Prototype
si4 encrypt_records(FILE_PROCESSING_STRUCT *fps);
Encrypts records if currently decrypted to the level specified in the record header. It
marks encrypted records as encrypted (positive of encryption level) in the record
header. If the number of records is known (stored in the universal header
number_of_entries field), this value is used, if this is set to
UNKNOWN_NUMBER_OF_ENTRIES the function will work as long as the
FILE_PROCESSING_STRUCT’s raw_data_bytes field reflects an integral number of
records.
The function also applies or removes the recording time offset to the times in the record
headers according to the value of the the MEF_global recording_time_offset_mode.
FUNCTION: extract_path_parts()
// Prototype
extract_path_parts(si1 *full_file_name, si1 *path, si1 *name, si1 *extension);
example:
SESSION session;
si1 *passed_session_directory = “/Data/Session_1.mefd”
FUNCTION: extract_terminal_password_bytes()
// Prototype
si4 extract_terminal_password_bytes(si1 *password, si1 *password_bytes);
UTF-8 passwords can contain up to 4 bytes per character. In UTF-8 encoding, the most
unique byte in each character is the terminal byte. This function extracts those bytes
from the UTF-8 password (passed in password) to password_bytes, which is used to
generate the encryption key for the AES algorithms. Unused bytes are zeroed. This
function is called by process_password_data().
FUNCTION: fill_empty_password_bytes()
// Prototype
void fill_empty_password_bytes(si1 *password_bytes);
Zero-value bytes at end of the password_bytes array are replaced with replicable
pseudo-random values generated by the included MEF library function random_byte().
This function is not currently used in the library, but can be used to strengthen weak
passwords, although as MEF is open source, the determined hacker could overcome
this measure.
FUNCTION: find_discontinuity_indices()
// Prototype
si8 *find_discontinuity_indices(TIME_SERIES_INDEX *tsi, si8 num_disconts, si8
number_of_blocks);
FUNCTION: find_discontinuity_samples()
// Prototype
si8 *find_discontinuity_samples(TIME_SERIES_INDEX *tsi, si8 num_disconts, si8
number_of_blocks, si1 add_tail);
FUNCTION: force_behavior()
// Constants
#define RESTORE_BEHAVIOR -1
// Prototype
void force_behavior(si4 behavior);
example: force RETURN_ON_FAIL for a function call, and then restore original value
force_behavior(RETURN_ON_FAIL);
function_whose_failure_can_be_handled();
force_behavior(RESTORE_BEHAVIOR);
FUNCTION: fps_close()
// Prototype
void fps_close(FILE_PROCESSING_STRUCT *fps);
Closes the file associated with the FPS’s FILE pointer and sets it to NULL. It also sets
the FPS’s file descriptor to -1 (closed file).
FUNCTION: fps_lock()
// Constants
#define FPS_NO_LOCK_TYPE ~(F_RDLCK | F_WRLCK | F_UNLCK)
// from <fcntl.h>
#define FPS_NO_LOCK_MODE 0
#define FPS_READ_LOCK_ON_READ_OPEN 1
#define FPS_WRITE_LOCK_ON_READ_OPEN 2
#define FPS_WRITE_LOCK_ON_WRITE_OPEN 4
#define FPS_WRITE_LOCK_ON_READ_WRITE_OPEN 8
#define FPS_READ_LOCK_ON_READ 16
#define FPS_WRITE_LOCK_ON_WRITE 32
// Prototype
si4 fps_lock(FILE_PROCESSING_STRUCT *fps, si4 lock_type, const si1 *function, si4 line,
ui4 behavior_on_fail);
Sets an advisory lock on the file specified by the FPS directive’s lock_mode. The lock is
set in blocking mode (i.e. it waits until a lock can be obtained). lock_type specifies
either a read or write lock. The function & line arguments are provided to know where
the function was called from in the case of failure.
FUNCTION: fps_open()
// Constants
#define FPS_NO_OPEN_MODE 0
#define FPS_R_OPEN_MODE 1
#define FPS_R_PLUS_OPEN_MODE 2
#define FPS_W_OPEN_MODE 4
#define FPS_W_PLUS_OPEN_MODE 8
#define FPS_A_OPEN_MODE 16
#define FPS_A_PLUS_OPEN_MODE 32
#define FPS_GENERIC_READ_OPEN_MODE (FPS_R_OPEN_MODE |
FPS_R_PLUS_OPEN_MODE |
FPS_W_PLUS_OPEN_MODE |
FPS_A_PLUS_OPEN_MODE)
#define FPS_GENERIC_WRITE_OPEN_MODE (FPS_R_PLUS_OPEN_MODE |
FPS_W_OPEN_MODE |
FPS_W_PLUS_OPEN_MODE |
FPS_A_OPEN_MODE |
FPS_A_PLUS_OPEN_MODE)
// Prototype
si4 fps_open(FILE_PROCESSING_STRUCT *fps, const si1 *function, si4 line,
ui4 behavior_on_fail);
Opens the file specified by the FPS according to the FPS directive open_mode. If the
mode permits file creation, the file will be created. If higher level directories are needed
to open the file in the specified location, they too are created. Once open, the file is
optionally locked according to the FPS directive’s lock_mode. The file descriptor and file
length are also updated.
FUNCTION: fps_read()
// Prototype
si4 fps_read(FILE_PROCESSING_STRUCT *fps, const si1 *function, si4 line,
ui4 behavior_on_fail);
Reads bytes specified by the FPS directive’s io_bytes (or more commonly the full file if
this is set to FPS_FULL_FILE). If lock_on_read is specified in the FPS directive’s
lock_mode, the file will be locked prior to the read and unlocked after the read.
FUNCTION: fps_unlock()
// Prototype
si4 fps_unlock(FILE_PROCESSING_STRUCT *fps, const si1 *function, si4 line,
ui4 behavior_on_fail);
Releases the advisory lock on the file specified by the FPS. The function & line
arguments are provided to know where the function was called from in the case of
failure.
FUNCTION: fps_write()
// Prototype
si4 fps_write(FILE_PROCESSING_STRUCT *fps, const si1 *function, si4 line,
ui4 behavior_on_fail);
Writes bytes specified by the FPS directive’s io_bytes (or more commonly the full file if
this is set to FPS_FULL_FILE). If lock_on_write is the specified in the FPS directive’s
lock_mode, the file will be locked prior to the write and unlocked after the write. The file
descriptor and file length are also updated.
FUNCTION: free_channel()
// Prototype
void free_channel(CHANNEL *channel, si4 free_channel_structure);
Frees all the memory pointed to by a CHANNEL structure including all memory
associated with SEGMENT structures within it. if free_channel_structure is set to
MEF_TRUE, the passed CHANNEL structure will itself be freed also.
FUNCTION: free_file_processing_struct()
// Prototype
void free_file_processing_struct(FILE_PROCESSING_STRUCT *fps);
FUNCTION: free_segment()
// Prototype
void free_segment(SEGMENT *segment, si4 free_segment_structure);
FUNCTION: free_session()
// Prototype
void free_session(SESSION *session, si4 free_session_structure);
Frees all the memory pointed to by a SESSION structure including all memory
associated with CHANNEL structures within it, and the SEGMENT structures within
them. If free_session_structure is set to MEF_TRUE, the passed SESSION structure
will itself be freed also.
FUNCTION: generate_file_list()
// Prototype
si1 **generate_file_list(si1 **file_list, si4 *num_files, si1 *enclosing_directory, si1
*extension)
Creates a list of files in the enclosing_directory with the specified extension. If file_list is
not NULL, it is presumed to be allocated, otherwise it will be allocated and it is the
calling function’s responsibility to free it. The function can also be used to generate a list
of directories with a specified extension. The number of files or directories in the list is
returned in num_files.
FUNCTION: generate_hex_string()
// Prototype
si1 *generate_hex_string(ui1 *bytes, si4 num_bytes, si1 *string);
Creates a hexadecimal string from “num_bytes” of the bytes in “bytes” into the string
pointed to by “string”. If string is NULL, it will be allocated. The length of the string
required is: (num_bytes + 1) * 3. This is conveniently generated by the macro
HEX_STRING_BYTES().
example 1:
ui1 hex_str[HEX_STRING_BYTES(ENCRYPTION_KEY_BYTES)];
example 2:
ui1 *hex_str;
FUNCTION: generate_recording_time_offset()
// Constants
#define USE_SYSTEM_TIME -1
#define MAXIMUM_GMT_OFFSET 86400
#define MINIMUM_GMT_OFFSET -86400
// Prototype
si8 generate_recording_time_offset(si8 recording_start_time_uutc, si4 GMT_offset);
T h e f u n c t i o n c a l c u l a t e s t h e r e c o r d i n g t i m e o ff s e t f r o m t h e p a s s e d
recording_start_time_uutc and GMT_offset. The result is stored in the MEF_globals
v a r i a b l e s r e c o r d i n g _ t i m e _ o f f s e t a n d G M T _ o f f s e t , r e s p e c t i v e l y. I f
recording_start_time_uutc equals USE_SYSTEM_TIME, the recording time offset and
GMT will be obtained from the system settings, and recording is assumed to start at the
time of the function call.
The GMT offset is the number of seconds (not hours) the recording time zone is offset
from GMT at the time of recording start. Its range is MINIMUM_GMT_OFFSET to
MAXIMUM_GMT_OFFSET. If GMT_offset is outside this range, it’s value will be set to
zero, and an error will be generated.
example:
#define CST_OFFSET_HOURS -6
FUNCTION: generate_segment_name()
// Prototype
si1 *generate_segment_name(FILE_PROCESSING_STRUCT *fps, si1 *segment_name);
A simple convenience function to generate the segment name from the channel name
and segment number in the FPS’s universal header. The result is stored in
segment_name if it is not NULL. The result is allocated and returned otherwise. If
allocated, the calling function is responsible for freeing it.
FUNCTION: generate_UUID()
// Prototype
ui1 *generate_UUID(ui1 *uuid);
Assigns 16 random bytes to the passed uuid buffer. The possibility of 16 zero bytes is
excluded as this is the NO_ENTRY value for UUIDs. The result is stored in uuid if it is
not NULL. The result is allocated and returned otherwise. If allocated, the calling
function is responsible for freeing it.
example:
generate_UUID(universal_header->level_UUID);
FUNCTION: initialize_file_processing_directives()
// Prototype
FILE_PROCESSING_DIRECTIVES *initialize_file_processing_directives(
FILE_PROCESSING_DIRECTIVES *directives);
FUNCTION: initialize_MEF_globals()
// Prototype
void initialize_MEF_globals();
The MEF_GLOBALS are allocated to the global heap and initialized to their default
values. A global pointer to the MEF_GLOBALS structure is set whose name is
“MEF_globals”. These globals are used by many functions in the library. It includes
boolean fields stating whether structure alignment has been confirmed, lookup tables for
CRC calculation, UTF8 printing, AES encryption, and SHA hash functions, the session
recording time offset and GMT offset, and a verbose flag which if set will cause many
library functions to show the output of their processing.
initialize_MEF_globals();
FUNCTION: initialize_meflib()
// Prototype
si4 initialize_meflib();
Initializes MEF_globals to default values (if the MEF_globals pointer is NULL, which it is
at the launch of the library), checks CPU endianness, checks MEF structure alignments,
seeds the random number generator with the current time, sets the file creation umask,
and a loads the CRC, UTF8, AES, and SHA lookup tables into the global heap (not
stack). Returns MEF_TRUE if all structures are aligned, MEF_FALSE if not. The
function currently exits if the cpu endianness is not little endian. This can be changed if
there is a demand for big endian processing going forward.
example 1:
if (initialize_meflib() == MEF_FALSE) {
fprintf(stderr, “error initializing meflib => exiting\n”)
exit(1);
}
MEF_globals->verbose = MEF_TRUE; // globals initialized by initialize_meflib(),
// default verbose setting is MEF_FALSE
example 2:
This example initializes MEF_globals to their default values. It then sets verbose to
MEF_TRUE. Because MEF_globals is not NULL, initialize_meflib() will not call
initialize_MEF_globals(), allowing verbose output of initialization routines, and
preserving any other non-default global setting changes that were made.
FUNCTION: initialize_metadata()
// Prototype
METADATA *initialize_metadata(METADATA *md);
The function sets all fields in a METADATA structure to their NO_ENTRY values. No
encryption is performed. Section 2 fields are set according to the FPS/s file_type_code.
example:
(void) initialize_metadata(metadata_fps);
FUNCTION: initialize_universal_header()
// Prototype
si4 initialize_universal_header(FILE_PROCESSING_STRUCT *fps, si1 generate_level_UUID, si1
generate_file_UUID, si1 originating_file);
// Constants
#define NO_UUID 0
The function sets universal header fields to default values. It will generate the
appropriate UUIDs if generate_level_UUID or generate_file_UUID are set to
MEF_TRUE. If originating_file is set to MEF_TRUE, the provenance_UUID will be set
to the value of the file_UUID. It fills in the current library’s MEF version and endianness.
example:
FUNCTION: local_date_time_string()
// Prototype
void local_date_time_string(si8 uutc_time, si1 *time_str);
Returns a string with local date and time from a UUTC time. 32 bytes are required for
this string. If NULL is passed for the string, it will be allocated and the pointer to the
string returned, it is the calling function’s responsibility to free this memory.
If the recording time offset is applied and the value is set in MEF_globals, this will be
added to the UUTC time. The GMT offset is also applied to obtain the local time. Note
that if a recording time offset is applied, but the reader has no access to these values,
the global values of both will be zero ( set in initialize_MEF_globals() ). And so the time
returned will be the true local time of day at the time of recording, but with recording
beginning on Jan 1, 1970 in GMT.
If recording time offset is not applied, its global value is zero, but the GMT offset is still
required to know the local time of day. Only if the global GMT offset is set correctly
can the function will return the correct local time of day.
example:
si1 time_str[32]; // all 32 bytes are required
local_date_time_string(universal_header->start_time, time_str);
FUNCTION: MEF_pad()
// Prototype
si8 MEF_pad(ui1 *buffer, si8 content_len, ui4 alignment);
Fills buffer beyond content_len (in bytes) with PAD_BYTE_VALUE, to next boundary
determined by alignment. Returns (content_len + pad_bytes).
FUNCTION: MEF_snprintf()
// Prototype
void MEF_snprintf(si1 *target, si4 target_field_bytes, si1 *format, …);
A version of snprintf() that zeros the unused bytes in the target field. Called as standard
sprintf() with the extra parameter target_field_bytes that specifies the length of the field
being written to. MEF strings are zeroed in unused bytes to facilitate identical CRCs in
files with identical information content.
example:
Prints full path to session directory into full_file_name field. Zeros unused bytes.
FUNCTION: MEF_sprintf()
// Prototype
void MEF_sprintf(si1 *target, si4 target_field_bytes, si1 *format, …);
A version of sprintf() that returns the number of characters copied including the
terminating zero. Useful in calculating pad-bytes needed in record data fields. See
MEF_pad();
FUNCTION: MEF_strcat()
// Prototype
si4 MEF_strcat(si1 *target_string, si1 *source_string);
A version of strcat() that returns the number of characters in the concatenated string
including the terminating zero. Useful in calculating pad-bytes needed in record data
fields. See MEF_pad();
FUNCTION: MEF_strcpy()
// Prototype
si4 MEF_strcpy(si1 *target_string, si1 *source_string);
A version of strcpy() that returns the number of characters copied including the
terminating zero. Useful in calculating pad-bytes needed in record data fields. See
MEF_pad();
FUNCTION: MEF_strncat()
// Prototype
void MEF_strncat(si1 *target_string, si1 *source_string, si4 target_field_bytes);
A version of strcat() that zeros the unused bytes in the target field. Called as standard
strncat(). MEF strings are zeroed in unused bytes to facilitate identical CRCs in files
with identical information content.
FUNCTION: MEF_strncpy()
// Prototype
void MEF_strncpy(si1 *target_string, si1 *source_string, si4 target_field_bytes);
A version of strncpy() that zeros the unused bytes in the target field. Called as standard
strncpy(). MEF strings are zeroed in unused bytes to facilitate identical CRCs in files
with identical information content.
example:
MEF_strncpy(metadata_fps->universal_header->channel_name, channel_name,
MEF_BASE_FILE_NAME_BYTES);
Copy channel_name into universal header channel_name field, zeroing unused bytes.
FUNCTION: numerical_fixed_width_string()
// Prototype
si1 *numerical_fixed_width_string(si1 *string, si4 string_bytes, si4 number);
Writes into string, string_bytes total digits, including prepended zeroes, the value of
number. String must be able to accommodate (string_bytes + 1) bytes. If string is NULL,
it will be allocated and the pointer to it will be returned. The calling function is
responsible for freeing this memory.
example:
si4 seg_number = 2;
si1 seg_number_string[FILE_NUMBERING_DIGITS + 1];
FUNCTION: offset_time_series_index_times()
// Prototype
si4 offset_time_series_index_times(FILE_PROCESSING_STRUCT *fps, si4 action);
// Constants
#define RTO_INPUT_ACTION 1
#define RTO_OUTPUT_ACTION 2
This function applies the recording time offset to the array of time series indices
according to the global recording_time_offset_mode and whether the operation is being
done as input or output. This is specified in the passed action parameter, with either the
constant RTO_INPUT_ACTION or RTO_OUTPUT_ACTION. This function is called by
read_MEF_file() and write_MEF_file() and usually needn’t be called explicitly.
FUNCTION: offset_video_index_times()
// Prototype
si4 offset_video_index_times(FILE_PROCESSING_STRUCT *fps, si4 action);
// Constants
#define RTO_INPUT_ACTION 1
#define RTO_OUTPUT_ACTION 2
This function applies the recording time offset to the array of video indices according to
the global recording_time_offset_mode and whether the operation is being done as
input or output. This is specified in the passed action parameter, with either the
constant RTO_INPUT_ACTION or RTO_OUTPUT_ACTION. This function is called by
read_MEF_file() and write_MEF_file() and usually needn’t be called explicitly.
FUNCTION: process_password_data()
// Prototype
PASSWORD_DATA *process_password_data(si1 *unspecified_password, si1 *level_1_password, si1
*level_2_password, UNIVERSAL_HEADER *universal_header);
// Structures
typedef struct {
ui1 level_1_encryption_key[ENCRYPTION_KEY_BYTES];
ui1 level_2_encryption_key[ENCRYPTION_KEY_BYTES];
ui1 access_level;
} PASSWORD_DATA;
example 1:
fps->password_data = process_password_data(password, NULL, NULL, fps->universal_header);
example 2:
In writing a new MEF file, a level 1 and level 2 password are passed, and their
password validation fields are written into the universal header. Both level 1 and level 2
encryption keys are generated into their appropriate fields in the PASSWORD_DATA
structure.
FUNCTION: proportion_filt()
// Prototype
void proportion_filt(sf8 *x, sf8 *px, si8 len, sf8 prop, si4 span);
Performs a sliding widow proportion filter from input array x to output array px of length
len. if px is NULL it will be allocated, and responibility for freeing this memory falls to the
calling function. The span is the window width in points and will be made odd if it is not.
The prop parameter varies between 0.0 and 1.0. A value of 0.0 in a local minimum filter,
0.5 is a median filter, and 1.0 is a local maximum filter. All other values in the range are
valid, so a value of 0.75 would give a filter of the local 75th percentile value in the
window.
FUNCTION: random_byte()
// Prototype
ui1 random_byte(ui4 *m_w, ui4 *m_z);
FUNCTION: read_MEF_channel()
// Prototype
CHANNEL *read_MEF_channel(CHANNEL *channel, si1 *chan_path, si4 channel_type, si1 *password,
PASSWORD_DATA *password_data, si1 read_time_series_data, si1 read_record_data)
// Channel Types
#define UNKNOWN_CHANNEL_TYPE -1
#define TIME_SERIES_CHANNEL_TYPE 1
#define VIDEO_CHANNEL_TYPE 2
// Structures
typedef struct {
si4 channel_type;
METADATA metadata;
FILE_PROCESSING_STRUCT *record_data_fps;
FILE_PROCESSING_STRUCT *record_indices_fps;
si8 number_of_segments;
SEGMENT *segments;
si1 path[MEF_FULL_FILE_NAME_BYTES]; // full path to enclosing
// directory
si1 name[MEF_BASE_FILE_NAME_BYTES]; // just base name, no extension
si1 extension[TYPE_BYTES]; // channel directory extension
si1 session_name[MEF_BASE_FILE_NAME_BYTES]; // base name, no extension
ui1 level_UUID[UUID_BYTES];
si1 anonymized_name[UNIVERSAL_HEADER_ANONYMIZED_NAME_BYTES];
si8 maximum_number_of_records;
si8 maximum_record_bytes;
si8 earliest_start_time;
si8 latest_end_time;
} CHANNEL;
This function will read the channel pointed to by chan_path (full path to the channel
directory) and fill in the the fields in the CHANNEL structure. If a channel structure is not
passed (NULL passed), one will be allocated. Either a password, or PASSWORD_DATA
structure should be passed to read encrypted fields. In the case that no data is
encrypted or only unencrypted data is needed, NULL can be passed for both fields. If
the read_time_series_data flag is set to MEF_TRUE in the passed directives, each time
series segment’s data will be read into its SEGMENT structure’s time_series_data_fps
raw_data field after the segment data’s universal header; otherwise only the segment
data’s universal header will be read into this field. If directives are NULL, default
directives will be used.
The session_name and level_UUID fields are filled in, but are redundant with the
universal header information in the record data and record indices files, if present.
These fields are included in this structure because they are useful in functions that write
new channel files.
If read_record_data is set to MEF_TRUE, the all record data will be read into the
appropriate structure’s record_data_fps raw_data field after the record data’s universal
header and the file will be closed; otherwise only the record data’s universal header will
be read into this field and the file will be left open with the file pointer pointing to the next
byte after the universal header.
The metadata structure is the same as those contained in a segment FPS, but is not
part of an FPS. It contains summary information of the segment metadata files. Fields
whose values vary across segments and whose value cannot be expressed as a
maximum, etc. are filled with their NO_ENTRY values.
The passed channel_type parameter is used to determine the metadata type to expect.
If UNKNOWN_CHANNEL_TYPE is passed, the channel type is determined from the
channel path name by calling channel_type_from_path(). The channel_type is stored in
the CHANNEL structure.
The CHANNEL structure also keeps track of other metadata derived from universal
headers and processing.
example:
(void) read_MEF_channel(&session->channels[i], full_file_name, TIME_SERIES_CHANNEL_TYPE, NULL,
password_data, MEF_FALSE, MEF_FALSE);
This call reads the channel specified by full_file_name into a preallocated CHANNEL
structure. A PASSWORD_DATA data structure is passed, so a password is not required.
The read_time_series_data and read_recod_data flags are set to MEF_FALSE, so only
the universal headers will be read in from these files.
FUNCTION: read_MEF_file()
// Prototype
FILE_PROCESSING_STRUCT *read_MEF_file(FILE_PROCESSING_STRUCT *fps, si1 *file_name, si1 *password,
PASSWORD_DATA *password_data, FILE_PROCESSING_DIRECTIVES *directives, ui4
behavior_on_fail)
// Structures
typedef struct {
si1 full_file_name[MEF_FULL_FILE_NAME_BYTES]; // full path
FILE *fp; // FILE pointer
si4 fd; // FILE descriptor
si8 file_length;
ui4 file_type_code;
UNIVERSAL_HEADER *universal_header;
FILE_PROCESSING_DIRECTIVES directives;
PASSWORD_DATA *password_data;
METADATA metadata;
TIME_SERIES_INDEX *time_series_indices;
VIDEO_INDEX *video_indices;
ui1 *records;
RECORD_INDEX *record_indices;
ui1 *RED_blocks;
si8 raw_data_bytes;
ui1 *raw_data;
} FILE_PROCESSING_STRUCT;
typedef struct {
si1 close_file;
si1 free_password_data; // when freeing FPS
si8 io_bytes; // bytes to read or write
ui4 lock_mode;
ui4 open_mode;
} FILE_PROCESSING_DIRECTIVES;
// Constants
#define FPS_FULL_FILE -1
The function reads any MEF file type, identified by its full path in file_name, into a
FILE_PROCESSING_STRUCT (FPS). If NULL is passed for the FPS one will be
allocated. If an FPS is allocated, and the passed directives are not NULL, they will be
used. If the FPS’s full_file_name field is NULL the passed file_name will be copied into
this field. The file will be opened if it is not already open. If the close_file directive is set
to MEF_FALSE, the file will be left open, otherwise it will be closed after reading. If the
io_bytes parameter is set to FPS_FULL_FILE the whole file will be read, otherwise only
io_bytes bytes will be read.
The data are read into the raw_data field of the FPS. The FPS’s universal_header
pointer is set to point to the beginning of the raw data. The appropriate file type’s
structure pointer in the FPS is set to point to the raw data after the universal header.
Read_MEF_file() validates file CRCs according to the global CRC_mode. It the decrypt
encrypted data to the access level allowed by the password data. It then offsets times
according to the global recording_time_offset_mode.
example 1:
Reads the time series data file pointed to by full_file_name. Read_MEF_file() allocates
and returns a pointer to the FPS. A PASSWORD_DATA structure is supplied, so
password is not processed, and need not be passed. All data are read in as the
io_bytes default is FPS_FULL_FILE. The file is closed after reading as the close_file
directive’s default is MEF_TRUE.
example 2:
segment->time_series_data_fps = allocate_file_processing_struct(0,
TIME_SERIES_DATA_FILE_TYPE_CODE, NULL, 0);
segment->time_series_data_fps->directives.io_bytes = UNIVERSAL_HEADER_BYTES
segment->time_series_data_fps->directives.close_file = MEF_FALSE; // default is MEF_TRUE
(void) read_MEF_file(segment->segment_data_fps, full_file_name, password, NULL, 0);
Reads the time series data file pointed to by full_file_name. Read_MEF_file() does not
allocate the FPS since one is passed. Preallocation was done to change the default
values of the directives; in this case to read just the universal header and leave the file
open with the file pointer pointing to the next byte after the universal header. A
PASSWORD_DATA structure was not supplied, so password is processed as an
unspecified password, and a PASSWORD_DATA structure is created for the FPS. If
passwords are preserved across MEF files - the returned PASSWORD_DATA can be
passed in future calls to read_MEF_file().
FUNCTION: read_MEF_segment()
// Prototype
SEGMENT *read_MEF_segment(SEGMENT *segment, si1 *seg_path, si4 channel_type, si1 *password,
PASSWORD_DATA *password_data, si1 read_time_series_data, si1 read_record_data)
// Structure
typedef struct {
si4 channel_type;
FILE_PROCESSING_STRUCT *metadata_fps;
FILE_PROCESSING_STRUCT *time_series_data_fps;
FILE_PROCESSING_STRUCT *time_series_indices_fps;
FILE_PROCESSING_STRUCT *video_indices_fps;
FILE_PROCESSING_STRUCT *record_data_fps;
FILE_PROCESSING_STRUCT *record_indices_fps;
si1 name[MEF_SEGMENT_BASE_FILE_NAME_BYTES]; // base name
si1 path[MEF_FULL_FILE_NAME_BYTES]; // full path to enclosing
//directory (channel dir)
si1 channel_name[MEF_BASE_FILE_NAME_BYTES]; // base name
si1 session_name[MEF_BASE_FILE_NAME_BYTES]; // base name
ui1 level_UUID[UUID_BYTES];
} SEGMENT;
This function will read the segment pointed to by seg_path (full path to the segment
directory) and fill in the the fields in the SEGMENT structure. If a segment structure is
not passed (NULL passed), one will be allocated. Either an unspecified password, or
PASSWORD_DATA structure should be passed to read encrypted fields. In the case
that no data is encrypted or only unencrypted data is needed, NULL can be passed for
both fields.
The passed channel_type parameter is used to determine the metadata type to expect.
If UNKNOWN_CHANNEL_TYPE is passed, the channel type is determined from the
channel path name by calling channel_type_from_path(). The channel_type is stored in
the CHANNEL structure.
The channel_name, session_name, and level_UUID fields are filled in, but are
redundant with the universal header information in each of the files. These fields are
included in this structure because they are useful in functions that write new segment
files.
If read_record_data is set to MEF_TRUE, the segment’s records data will be read into
the SEGMENT structure’s records_data_fps raw_data field after the records data’s
universal header and the file will be closed; otherwise only the records data’s universal
header will be read into this field and the file will be left open with the file pointer
pointing to the next byte after the universal header.
example:
SEGMENT *segment;
This call will read the all the files of the segment pointed to by full_file_name and
allocate and populate a SEGMENT structure. The passed password_data is assigned in
the FILE_PROCESSING_STRUCTs. The time series data file is opened, read in full,
and closed. Likewise for the segment record data file, if present. This is an uncommon
use for large data files as reading all of the data into memory is frequently impractical.
FUNCTION: read_MEF_session()
// Prototype
SESSION *read_MEF_session(SESSION *session, si1 *sess_path, si1 *password, PASSWORD_DATA
*password_data, si1 read_time_series_data, si1 read_record_data);
typedef struct {
METADATA time_series_metadata;
si4 number_of_time_series_channels;
CHANNEL *time_series_channels;
METADATA video_metadata;
si4 number_of_video_channels;
CHANNEL *video_channels;
FILE_PROCESSING_STRUCT *record_data_fps;
FILE_PROCESSING_STRUCT *record_indices_fps;
si1 name[MEF_BASE_FILE_NAME_BYTES]; // just base name, no extension
si1 path[MEF_FULL_FILE_NAME_BYTES]; // path to enclosing directory
si1 anonymized_name[UNIVERSAL_HEADER_ANONYMIZED_NAME_BYTES];
ui1 level_UUID[UUID_BYTES];
si8 maximum_number_of_records;
si8 maximum_record_bytes;
si8 earliest_start_time;
si8 latest_end_time;
} SESSION;
This function will read all the files associated with the session pointed to by sess_path
(full path to the session directory) and fill in the the fields in the SESSION structure. If a
SESSION structure is not passed (NULL passed), one will be allocated. Either an
unspecified password, or PASSWORD_DATA structure should be passed to read
encrypted fields. In the case that no data is encrypted or only unencrypted data is
needed, NULL can be passed for both fields.
The level_UUID field is filled in, but is redundant with the universal header information in
the record data and indices files, if present. This field is included in this structure
because it is useful in functions that write new session files.
If the directive’s read_time_series_data flag is set to MEF_TRUE, the segment data will
be read into the SEGMENT structure’s data_fps raw_data field after the segment data’s
universal header and the file will be closed; otherwise only the segment data’s universal
header will be read into this field and the file will be left open with the file pointer
pointing to the next byte after the universal header.
If the read_record_data directive is set to MEF_TRUE, the all records data files will be
read into the appropriate structure’s records_data_fps raw_data field after the records
data’s universal header and the file will be closed; otherwise only the records data’s
universal header will be read into this field and the file will be left open with the file
pointer pointing to the next byte after the universal header.
The metadata structures are the same as those contained in CHANNEL structures; they
are not part of an FPS. It contains summary information of the channel metadata files.
Fields whose values vary across channels and whose value cannot be expressed as a
maximum, etc. are filled with their NO_ENTRY values.
The SESSION structure also keeps track of other metadata derived from universal
headers and processing.
example:
SESSION *session;
This call will allocate a SESSION structure and read all files associated with a MEF
session and fill in the fields of at the SESSION structure and all of its substructures. It
will not read the segment data, or record data unless these flags are set in the passed
FILE_PROCESSING_DIRECTIVES. The universal headers of those files will be read,
and the files will be left open. Their file pointers will be left at the beginning of the data
after the universal header. All other files will be read completely into their
FILE_PROCESSING_STRUCTs and closed.
FUNCTION: reallocate_file_processing_struct()
// Prototype
si4 reallocate_file_processing_struct(FILE_PROCESSING_STRUCT *fps, si8 raw_data_bytes);
FUNCTION: remove_line_noise()
// Prototype
si4 remove_line_noise(si4 *data, si8 n_samps, sf8 sampling_frequency, sf8 line_frequency, sf8
*template)
AC line noise is removed from the input data array via template subtraction. If *template
is not NULL the subtracted template will be returned in that array. If NULL is passed for
*template, it will be allocated and freed. The template does not adapt so the function is
best used on small chunks of data, such as individual RED blocks prior to compression.
The function remove_line_noise_adaptive() does adaptive filtering and does not return a
template. The noise suppression with that function is generally better, but it is slower
and does not return a template. The advantage of returning a template is that the
template can be stored for each block of data so that if needed the unmodified data can
be restored. There is a record type LNPT (line noise template) that was designed for
this purpose, and would be stored in segment-level record files.
FUNCTION: remove_line_noise_adaptive()
// Prototype
void remove_line_noise(si4 *data, si8 n_samps, sf8 sampling_frequency, sf8 line_frequency, si4
n_cycles)
AC line noise is removed from the input data array via template subtraction. The
template adapts at a rate specified by n_cycles.
FUNCTION: remove_recording_time_offset()
// Prototype
void remove_recording_time_offset(si8 *time);
The global recording time offset is removed from the passed µUTC time. If the value is
positive, it is presumed not to have had the recording time offset applied, and nothing is
done. The converse function is apply_recording_time_offset(), described above.
FUNCTION: show_file_processing_struct()
// Prototype
void show_file_processing_struct(FILE_PROCESSING_STRUCT *fps)
// Structures
typedef struct {
si1 full_file_name[MEF_FULL_FILE_NAME_BYTES]; // full path
FILE *fp;
si4 fd;
si8 file_length;
ui4 file_type_code;
UNIVERSAL_HEADER *universal_header;
FILE_PROCESSING_DIRECTIVES directives;
PASSWORD_DATA *password_data;
METADATA metadata;
TIME_SERIES_INDEX *time_series_indices;
VIDEO_INDEX *video_indices;
ui1 *records;
RECORD_INDEX *record_indices;
ui1 *RED_blocks;
si8 raw_data_bytes;
ui1 *raw_data;
} FILE_PROCESSING_STRUCT;
FUNCTION: show_metadata()
// Prototype
void show_metadata(FILE_PROCESSING_STRUCT *fps)
// Structures
typedef struct {
METADATA_SECTION_1 *section_1;
TIME_SERIES_METADATA_SECTION_2 *time_series_section_2;
VIDEO_METADATA_SECTION_2 *video_section_2;
METADATA_SECTION_3 *section_3;
} METADATA;
Displays all the elements of a METADATA structure of the type specified by the passed
FILE_PROCESSING_STRUCT.
FUNCTION: show_password_data()
// Prototype
void show_password_data(FILE_PROCESSING_STRUCT *fps);
// Structures
typedef struct {
ui1 level_1_encryption_key[ENCRYPTION_KEY_BYTES];
ui1 level_2_encryption_key[ENCRYPTION_KEY_BYTES];
ui1 access_level;
} PASSWORD_DATA;
FUNCTION: show_record()
// Prototype
void show_record(RECORD_HEADER *record_header, ui4 record_number, PASSWORD_DATA *pwd);
This function displays the contents of the record pointed to by record_header. If the
record needs to be decrypted and the access level is sufficient, the record will be
decrypted. Show_record() resides in the mefrec.c file.
FUNCTION: show_records()
// Constant
#define UNKNOWN_NUMBER_OF_ENTRIES -1
// Prototype
void show_records(FILE_PROCESSING_STRUCT *fps);
This function displays the contents of the records data file. If the record needs to be
decrypted and the access level is sufficient, the record will be decrypted.
Show_records() calls show_record() for each record. Show_record() resides in the
mefrec.c file. If the number_of_records is known, this number will be used. Otherwise
(i.e. number_of_records == UNKNOWN_NUMBER_OF_ENTRIES) the function will still
work, but could fail in the case of an incomplete terminal record.
FUNCTION: show_universal_header()
// Prototype
void show_universal_header(FILE_PROCESSING_STRUCT *fps);
// Structure
typedef struct {
ui4 file_CRC;
si1 file_type_string[TYPE_BYTES];
ui1 mef_version_major;
ui1 mef_version_minor;
ui1 byte_order_code;
ui1 level_1_password_validation_field[PASSWORD_VALIDATION_FIELD_BYTES];
ui1 level_2_password_validation_field[PASSWORD_VALIDATION_FIELD_BYTES];
ui1 session_UUID[UUID_BYTES];
ui1 channel_UUID[UUID_BYTES];
ui1 segment_UUID[UUID_BYTES];
ui1 protected_region[UNIVERSAL_HEADER_PROTECTED_REGION_BYTES];
ui1 discretionary_region[UNIVERSAL_HEADER_DISCRETIONARY_REGION_BYTES];
} UNIVERSAL_HEADER;
FUNCTION: write_MEF_file()
// Prototype
si4 write_MEF_file(FILE_PROCESSING_STRUCT *fps);
The function will write out the file contained in the FILE_PROCESSING_STRUCT. If the
file is not yet open, it will be opened. If the file requires encryption it will be encrypted.
Times will be offset according to the global recording_time_offset_mode. The file CRCs
will be calculated according to the global CRC_mode and the entered into the universal
header.
If the io_bytes directive is set to FPS_FULL_FILE, the whole file will be written,
otherwise only this number of bytes will be written. If the close_file directive is set to
MEF_FALSE, the file will be left open.
/************************************************************************************/
/********************************** FILTER Functions ********************************/
/************************************************************************************/
// Constants
#define FILT_LOWPASS_TYPE 1
#define FILT_BANDPASS_TYPE 2
#define FILT_HIGHPASS_TYPE 3
#define FILT_BANDSTOP_TYPE 4
#define FILT_TYPE_DEFAULT FILT_LOWPASS_TYPE
#define FILT_ORDER_DEFAULT 5
#define FILT_MAX_ORDER 10
#define FILT_BAD_FILTER -1
typedef struct {
sf16 real;
sf16 imag;
} FILT_LONG_COMPLEX;
// Prototypes
void FILT_balance(sf16 **a, si4 poles);
si4 FILT_butter(FILT_PROCESSING_STRUCT *filtps);
void FILT_complex_divl(FILT_LONG_COMPLEX *a, FILT_LONG_COMPLEX *b,
FILT_LONG_COMPLEX *quotient);
void FILT_complex_expl(FILT_LONG_COMPLEX *exponent, FILT_LONG_COMPLEX *ans);
void FILT_complex_multl(FILT_LONG_COMPLEX *a, FILT_LONG_COMPLEX *b,
FILT_LONG_COMPLEX *product);
void FILT_elmhes(sf16 **a, si4 poles);
void FILT_filtfilt(FILT_PROCESSING_STRUCT *filtps);
void FILT_free_processing_struct(FILT_PROCESSING_STRUCT *filtps,
si1 free_orig_data, si1 free_filt_data);
FILT_PROCESSING_STRUCT *FILT_initialize_processing_struct(si4 order, si4 type, sf8 samp_freq,
si8 data_len, si1 alloc_orig_data, si1 alloc_filt_data,
sf8 cutoff_1, ...);
void FILT_generate_initial_conditions(FILT_PROCESSING_STRUCT *filtps);
void FILT_hqr(sf16 **a, si4 poles, FILT_LONG_COMPLEX *eigs);
void FILT_invert_matrix(sf16 **a, sf16 **inv_a, si4 order);
void FILT_mat_multl(void *a, void *b, void *product, si4 outer_dim1,
si4 inner_dim, si4 outer_dim2);
void FILT_unsymmeig(sf16 **a, si4 poles, FILT_LONG_COMPLEX *eigs);
The functions in the FILTER section of the library facilitate creation of Butterworth
infinite impulse response (IIR) filters and perform zero-phase digital filtering using them.
Many or the functions are purely internal to the filtering process, so only the gateway
functions will be described here.
FUNCTION: FILT_butter()
// Prototype
si4 FILT_butter(FILT_PROCESSING_STRUCT *filtps);
This function calculates coefficients for a Butterworth filter of the specified type and
returns the poles of the filter (which may be double of the order depending on filter type)
in the numerator and denominator fields of the FILT_PROCESSING_STRUCT. These
arrays are allocated in FILT_butter().
FUNCTION: FILT_filtfilt()
// Prototype
void FILT_filtfilt(FILT_PROCESSING_STRUCT *filtps)
This call non-destructively applies the specified filter to the orig_data (si4) array, and
returns the filtered data in the sf8_filt_data array. If the initial_conditions or sf8_buffer
arrays are NULL, they will be allocated and freed after use. The initial_conditions will be
calculated if they are not passed.
example:
FILT_PROCESSING_STRUCT *filtps;
RED_PROCESSING_STRUCT *rps;
// set up filter
filtps->order = 5;
filtps->type = FILT_BANDPASS_TYPE;
filtps->sampling_frequency = 32000.0;
filtps->cutoffs[0] = 100.0;
filtps->cutoffs[1] = 200.0;
FILT_butter(filtps);
FILT_generate_initial_conditions(filtps);
This code snippet applies a bandpass Butterworth filter (100 - 200 Hz band) to the
integer (si4) data in the FILT_PROCESSING_STRUCT’s orig_data array, returning the
results as floating point data (sf8) in the FILT_PROCESSING_STRUCT’s sf8_filt_data
array. See RED_apply_filter() for a function that does this and fills in the integer data
(si4) in the FILT_PROCESSING_STRUCT’s filt_data array.
FUNCTION: FILT_free_processing_struct()
// Prototype
void FILT_free_processing_struct(FILT_PROCESSING_STRUCT *filtps, si1 free_orig_data,
si1 free_filt_data);
example:
FILT_PROCESSING_STRUCT *filtps;
FUNCTION: FILT_initialize_processing_struct()
// Prototype
FILT_PROCESSING_STRUCT *FILT_initialize_processing_struct(si4 order, si4 type, sf8 samp_freq,
si8 data_len, si1 alloc_orig_data, si1 alloc_filt_data,
sf8 cutoff_1, ...);
FUNCTION: FILT_generate_initial_conditions()
// Prototype
void FILT_generate_initial_conditions(FILT_PROCESSING_STRUCT *filtps);
This function calculates and returns the initial conditions for a Butterworth filter of the
specified type.
/************************************************************************************/
/************************************ RED Functions *********************************/
/************************************************************************************/
// Structures
typedef struct {
ui4 block_CRC;
ui1 flags;
ui1 protected_region[RED_BLOCK_PROTECTED_REGION_BYTES];
ui1 discretionary_region[RED_BLOCK_DISCRETIONARY_REGION_BYTES];
sf4 detrend_slope;
sf4 detrend_intercept;
sf4 scale_factor;
ui4 difference_bytes;
ui4 number_of_samples;
ui4 block_bytes;
si8 start_time;
ui1 statistics[RED_BLOCK_STATISTICS_BYTES];
} RED_BLOCK_HEADER;
typedef struct {
si1 encryption_level; // encryption level for data blocks, passed in compression,
// returned in decompression
si1 discontinuity; // set if block is first after a discontinuity, passed in
// compression, returned in decompression
si1 detrend_data; // set if block is to be detrended (somewhat useful in lossless,
// more useful in lossy compression)
si1 return_lossy_data; // if set, lossy data returned in decompressed_data during
// lossy compression
si1 reset_discontinuity; // if discontinuity directive == MEF_TRUE, reset to
// MEF_FALSE after compressing the block
si1 require_normality; // in lossy compression, lossless compression will be
// performed in blocks whose samples are not approximately
// normally distributed
sf8 normal_correlation; // if require_normality is set, the correlation of the sample
// distribution with a normal distribution must be >= this
// number (range -1.0 to 1.0)
} RED_PROCESSING_DIRECTIVES;
typedef struct {
ui1 mode; // compression mode
sf8 goal_compression_ratio; // goal value passed
sf8 actual_compression_ratio; // actual value returned in RED_FIXED_COMPRESSION_RATIO
// mode
sf8 goal_mean_residual_ratio; // goal value passed
sf8 actual_mean_residual_ratio; // actual value returned in RED_MEAN_RESIDUAL_RATIO
// mode
sf8 goal_tolerance; // tolerance for lossy compression mode goal, value of <= 0.0
// uses default values, which are returned
si4 maximum_rounds_per_block; // maximum loops to attain goal compression
} RED_COMPRESSION_PARAMETERS;
typedef struct {
ui4 counts[RED_BLOCK_STATISTICS_BYTES + 1]; // used by
// RED_encode() & RED_decode()
PASSWORD_DATA *password_data; // passed in compression & decompression
RED_COMPRESSION_PARAMETERS compression;
RED_PROCESSING_DIRECTIVES directives;
si1 *difference_buffer; // passed in both compression &
// decompression
ui1 *compressed_data; // passed in decompression, returned in
// compression, should not be updated
RED_BLOCK_HEADER *block_header; // points to beginning of current block
// within compressed_data array, updatable
si4 *decompressed_data; // returned in decompression or if
// lossy data requested, used in some
// compression modes, should not be updated
si4 *decompressed_ptr; // points to beginning of current block
// within decompressed_data array, updatable
si4 *original_data; // passed in compression, should not be
// updated
si4 *original_ptr; // points to beginning of current block
// within original_data array, updatable
si4 *detrended_buffer; // used if needed in compression, size
// of decompressed block
si4 *scaled_buffer; // used if needed in compression, size of
// decompressed block
} RED_PROCESSING_STRUCT;
// Macros
#define RED_MAX_DIFFERENCE_BYTES(x) (x * 5) // full si4 plus 1 keysample flag byte per
// sample
#define RED_MAX_COMPRESSED_BYTES(x, y) ((RED_MAX_DIFFERENCE_BYTES(x) +
RED_BLOCK_HEADER_BYTES + 7) * y) // no compression
// plus header plus maximum pad bytes, for y blocks
FUNCTION: RED_allocate_processing_struct()
// Prototype
RED_PROCESSING_STRUCT *RED_allocate_processing_struct(si8 original_data_size, si8
compressed_data_size, si8 decompressed_data_size, si8 difference_buffer_size, si8
detrended_buffer_size, si8 scaled_buffer_size, PASSWORD_DATA *password_data);
example:
rps = RED_allocate_processing_struct(max_samps, RED_MAX_COMPRESSED_BYTES(max_samps, 1), 0,
RED_MAX_DIFFERENCE_BYTES(max_samps), 0, 0, pwd);
FUNCTION: RED_calculate_mean_residual_ratio()
// Prototype
sf8 RED_calculate_mean_residual_ratio(si4 *original_data, si4 *lossy_data, ui4 n_samps);
Calculates and returns the mean residual ratio between the original_data and
lossy_data buffers. Used in the MEAN_RESIDUAL_RATIO compression mode.
FUNCTION: RED_check_RPS_allocation()
// Prototype
si1 RED_check_RPS_allocation(RED_PROCESSING_STRUCT *rps);
Checks that the appropriate buffers are allocated in an RPS for the type of operation
being performed. The operation is determined by the values of the members of the
RPS’s compression and directives structures. It returns MEF_TRUE if the appropriate
buffers are allocated and MEF_FALSE if not unless the behavior_on_fail global is set to
exit. Deficient allocations are printed to stderr, as are unnecessarily allocated buffers.
This function may used if the programmer is uncertain which buffers to allocate for
specific compression & decompression requirements. It is not called by any of the other
functions in the library and must be called independently.
example:
RED_PROCESSING_STRUCT *rps;
si1 allocate_decompressed_data_buffer;
rps->compression.mode = RED_FIXED_COMPRESSION_RATIO;
force_behavior(RETURN_ON_FAIL);
RED_check_RPS_allocation(rps);
force_behavior(RESTORE_BEHAVIOR);
FUNCTION: RED_decode()
// Prototype
void RED_decode(RED_PROCESSING_STRUCT *rps);
Detrends data from input_buffer to output_buffer. The detrended slope and intercept
values entered into RPS’s block_header. If the input_buffer == output_buffer detrending
is done in place.
FUNCTION: RED_encode()
// Prototype
void RED_encode(RED_PROCESSING_STRUCT *rps);
Compress data from original_ptr to block_header pointer (compressed data array). This
is the main entry point into the library’s compression routines.
FUNCTION: RED_encode_exec()
// Prototype
void RED_encode_exec(RED_PROCESSING_STRUCT *rps, si4 *input_buffer, si1 input_is_detrended);
FUNCTION: RED_encode_lossy()
// Prototype
void RED_encode_lossy(RED_PROCESSING_STRUCT *rps);
// Constants
#define RED_LOSSLESS_COMPRESSION 0 // lossless (default)
#define RED_FIXED_SCALE_FACTOR 1 // apply this scale factor to the block,
// 1.0 results in lossless compression;
#define RED_FIXED_COMPRESSION_RATIO 2 // e.g. 20% of original si4 size is 0.2 -
// if lossless satisfies, no
// compression is done
#define RED_MEAN_RESIDUAL_RATIO 3 // sum(abs((scaled_data -
// original_data))) /
// sum(abs(original_data)), e.g. 5%
// difference is 0.05
RED compress from original_ptr to block_header pointer (compressed data array),
according to the specified compression mode. If lossy data is to be returned in the
decompressed data buffer, this is generated. The function calls RED_encode_exec() for
the actual compression which is described above. It returns the number of bytes
(including pad bytes) in the compressed block.
example:
RED_PROCESSING_STRUCT *rps;
FUNCTION: RED_filter()
// Prototype
void RED_filter(FILT_PROCESSING_STRUCT *filtps);
Applies the filter specified by filtps to it’s original data field. The sf8_filt_data are
converted to si4s in the filt_data field. The filt_data field of the
FILT_PROCESSING_STRUCT can be assigned to the filtered data buffer of a
RED_PROCESSING_STRUCT for non-destructive filtering, or to the original data field
for destructive filtering, with memory conservation.
FUNCTION: RED_find_extrema()
// Prototype
void RED_find_extrema(si4 *buffer, si8 number_of_samples, TIME_SERIES_INDEX *tsi);
Finds the extrema in buffer and enters them into their respective fields in a time series
index.
FUNCTION: RED_free_processing_struct()
FUNCTION: RED_free_processing_struct()
// Prototype
void RED_free_processing_struct(RED_PROCESSING_STRUCT *rps);
Frees any non-NULL buffer pointers in a RED_PROCESSING_STRUCT, then frees the
structure itself.
FUNCTION: RED_generate_lossy_data()
// Prototype
void RED_generate_lossy_data(RED_PROCESSING_STRUCT *rps, si4 *input_buffer, si4
*output_buffer, si1 input_is_detrended);
Generates lossy data from input_buffer to output_buffer using the detrained and scale
factors from the block_header. If the data is already detrended, it will not be done again.
If input_buffer == output_buffer, lossy data will be generated in place.
FUNCTION: RED_initialize_normal_CDF_table()
// Prototype
sf8 *RED_initialize_normal_CDF_table(si4 global_flag);
FUNCTION: RED_retrend()
// Prototype
si4 *RED_retrend(RED_PROCESSING_STRUCT *rps, si4 *input_buffer, si4 *output_buffer);
The function adds the trend specified by the block_header to the data from input_buffer
to output_buffer. If the input_buffer == output_buffer retrending is done in place.
FUNCTION: RED_round()
// Prototype
si4 RED_round(sf8 val);
FUNCTION: RED_scale()
// Prototype
si4 *RED_scale(RED_PROCESSING_STRUCT *rps, si4 *input_buffer, si4 *output_buffer);
FUNCTION: RED_show_block_header()
// Prototype
void RED_show_block_header(RED_BLOCK_HEADER *bh);
// Structure
typedef struct {
ui4 block_CRC;
ui1 flags;
ui1 protected_region[RED_BLOCK_PROTECTED_REGION_BYTES];
ui1 discretionary_region[RED_BLOCK_DISCRETIONARY_REGION_BYTES];
sf4 detrend_slope;
sf4 detrend_intercept;
sf4 scale_factor;
ui4 difference_bytes;
ui4 number_of_samples;
ui4 block_bytes;
si8 start_time;
ui1 statistics[RED_BLOCK_STATISTICS_BYTES];
} RED_BLOCK_HEADER;
FUNCTION: RED_test_normality()
// Prototype
sf8 RED_test_normality(si4 *data, ui4 n_samps);
Returns the Pearson correlation of the normalized cumulative distribution of the input
data to a pure normal cumulative distribution function (a variant of the Kolmogorov-
Smirnov test for normality).
FUNCTION: RED_unscale()
// Prototype
si4 *RED_unscale(RED_PROCESSING_STRUCT *rps, si4 *input_buffer, si4 *output_buffer);
Removes the scale data from input_buffer to output_buffer by the scale_factor in the
block_header. If input_buffer == output_buffer, unscaling will be done in place.
FUNCTION: RED_update_RPS_pointers()
// Prototype
RED_BLOCK_HEADER *RED_update_RPS_pointers(RED_PROCESSING_STRUCT *rps, ui1 flags);
// Constants
#define RED_UPDATE_ORIGINAL_PTR 1
#define RED_UPDATE_BLOCK_HEADER_PTR 2 // will also update block_header pointer
#define RED_UPDATE_DECOMPRESSED_PTR 4
Convenience function to update the RPS pointers specified by flags. The block_header
is updated by the block_header value block_bytes. Other pointers are updated by the
block_header value number_of_samples. The function is inline, so there is no extra
overhead. The block_header pointer is returned.
example:
ui1 flags;
RED_BLOCK_HEADER *block_header;
/************************************************************************************/
/************************************ CRC Utilities *********************************/
/************************************************************************************/
FUNCTION: CRC_calculate()
// Prototype
ui4 CRC_calculate(ui1 *block_ptr, ui4 block_bytes);
// Constant
#define CRC_START_VALUE 0xFFFFFFFF
is equivalent to:
Allocates and initializes the CRC table generated from the 32-bit Koopman polynomial
into heap space. If global_flag is set, the MEF_globals pointer CRC_table is also set to
this value. This function is called by initialize_meflib().
FUNCTION: CRC_update()
// Prototype
ui4 CRC_update(ui1 *block_ptr, ui4 block_bytes, ui4 current_crc);
Returns the CRC of block of size block_bytes, pointed to by block_ptr, starting CRC
value is passed in current_crc.
FUNCTION: CRC_validate()
// Prototype
si4 CRC_validate(ui1 *block_ptr, ui4 block_bytes, ui4 crc_to_validate);
Returns MEF_TRUE if the calculated CRC of the block pointed to by block_ptr matches
the value passed in crc_to_validate. If they do not match, MEF_FLASE is returned.
/************************************************************************************/
/*********************************** UTF-8 Utilities ********************************/
/************************************************************************************/
// Prototypes
si4 UTF8_escape(si1 *buf, si4 sz, si1 *src, si4 escape_quotes); // convert UTF-8 "src" to
// ASCII with escape sequences.
si4 UTF8_escape_wchar(si1 *buf, si4 sz, ui4 ch); // given a wide character, convert it to an
ASCII escape sequence stored in buf, where buf is "sz" bytes. returns the number of characters
output
si4 UTF8_f
uments may be in UTF-8. You can avoid this function and just use ordinary printf()
// if the current locale is UTF-8.
si1 *UTF8_memchr(si1 *s, ui4 ch, size_t sz, si4 *charn); // same as the above, but searches
// a buffer of a given size instead of a NUL-terminated string.
ui4 UTF8_nextchar(si1 *s, si4 *i); // return next character, updating an index variable
si4 UTF8_printf(si1 *fmt, ...); // printf() where the format string and arguments may be in
// UTF-8. You can avoid this function and just use ordinary printf() if the current
// locale is UTF-8.
si4 UTF8_read_escape_sequence(si1 *str, ui4 *dest); // assuming src points to the character
// after a backslash, read an escape sequence, storing the result in dest and returning
// the number of input characters processed
si1 *UTF8_strchr(si1 *s, ui4 ch, si4 *charn); // return a pointer to the first occurrence of
// ch in s, or NULL if not found. character index of found character returned in *charn.
si4 UTF8_toucs(ui4 *dest, si4 sz, si1 *src, si4 srcsz); // convert UTF-8 data to wide
// character
si4 UTF8_toutf8(si1 *dest, si4 sz, ui4 *src, si4 srcsz); // convert wide character to UTF-8
data
si4 UTF8_unescape(si1 *buf, si4 sz, si1 *src); // convert a string "src" containing escape
// sequences to UTF-8 if escape_quotes is nonzero, quote characters will be preceded by
// backslashes as well.
Not all of the UTF-8 functions are used in the library, but they are included in the library
for end-user and potential future use. Some of the included functions are used by other
UTF-8 functions, and thus require inclusion. Only those functions that are currently used
in other (non-UTF-8) meflib functions are described in this section.
FUNCTION: UTF8_initialize_offsets_from_UTF8_table()
// Prototype
ui4 *UTF8_initialize_offsets_from_UTF8_table(si4 global_flag);
Allocates and initializes the offsets_from_UTF8 table into heap space. If global_flag is
set, the MEF_globals pointer UTF8_offsets_from_UTF8_table is also set to this value.
This function is called by initialize_meflib().
FUNCTION: UTF8_initialize_trailing_bytes_for_UTF8_table()
// Prototype
si1 *UTF8_initialize_trailing_bytes_for_UTF8_table(si4 global_flag);
Allocates and initializes the trailing_bytes_for_UTF8 table into heap space. If global_flag
is set, the MEF_globals pointer UTF8_trailing_bytes_for_UTF8_table is also set to this
value. This function is called by initialize_meflib().
FUNCTION: UTF8_fprintf()
// Prototype
si4 UTF8_fprintf(FILE *stream, si1 *fmt, …);
FUNCTION: UTF8_nextchar()
// Prototype
ui4 UTF8_nextchar(si1 *s, si4 *i);
Returns the next character in the UTF-8 string s, updating the index variable i. Used by
extract_terminal_password_bytes().
FUNCTION: UTF8_printf()
// Prototype
si4 UTF8_strlen(si1 *s);
/************************************************************************************/
/************************************ AES Utilities *********************************/
/************************************************************************************/
// Function Prototypes
void AES_cipher(si4 Nr, ui1 *in, ui1 *out, ui1 state[][4], ui1 *RoundKey);
void AES_inv_cipher(si4 Nr, ui1 *in, ui1 *out, ui1 state[][4], ui1 *RoundKey);
Not all of the AES functions are used by the other functions in the library, but are used
by other AES functions, and thus require inclusion. Only those functions that are
currently used in other (non-AES) meflib functions are described in this section.
FUNCTION: AES_initialize_rcon_table()
// Prototype
si4 *AES_initialize_rcon_table(si4 global_flag);
Allocates and initializes the AES rcon table into heap space. If global_flag is set, the
MEF_globals pointer AES_rcon_table is also set to this value. This function is called by
initialize_meflib().
FUNCTION: AES_initialize_rsbox_table()
// Prototype
si4 *AES_initialize_rsbox_table(si4 global_flag);
Allocates and initializes the AES rsbox table into heap space. If global_flag is set, the
MEF_globals pointer AES_rsbox_table is also set to this value. This function is called by
initialize_meflib().
FUNCTION: AES_initialize_sbox_table()
// Prototype
si4 *AES_initialize_sbox_table(si4 global_flag);
Allocates and initializes the AES sbox table into heap space. If global_flag is set, the
MEF_globals pointer AES_sbox_table is also set to this value. This function is called by
initialize_meflib().
FUNCTION: AES_decrypt()
// Prototype
void AES_decrypt(ui1 *in, ui1 *out, si1 *password, ui1 *expanded_key);
Decrypts a 16 byte (128 bit) block of AES-128 encrypted data in the “in” buffer to the
“out” buffer. The decryption can be done in place (“in” equals “out”), and is most often
done this way within the library functions. Either expanded_key or password must be
non-NULL. If both are non-NULL, the expanded key will be used, as it is more efficient.
An expanded key can be obtained from the function AES_key_expansion(). If a
password is to be used, an expanded key is generated from it, used, and discarded. A
password is a 16 byte sequence. If, as is usually the case, this is a string, unused bytes
should be zeroed, as these bytes, while meaningless to the string, cannot vary for
reproducible decryption. If a UTF-8 string is used for a password, the meflib routines
extract the terminal (most unique) bytes from each character to be used as the
password bytes. This can be done with the function extract_terminal_password_bytes();
it is not done in this function.
FUNCTION: AES_encrypt()
// Prototype
void AES_encrypt(ui1 *in, ui1 *out, si1 *password, ui1 *expanded_key);
Encrypts a 16 byte (128 bit) block of data in the “in” buffer to the “out” buffer using the
AES-128 algorithm. The encryption can be done in place (“in” equals “out”), and is most
often done this way within the library functions. Either expanded_key or password must
be non-NULL. If both are non-NULL, the expanded key will be used, as it is more
efficient. An expanded key can be obtained from the function AES_key_expansion(). If a
password is to be used, an expanded key is generated from it, used, and discarded. A
password is a 16 byte sequence. If, as is usually the case, this is a string, unused bytes
should be zeroed, as these bytes, while meaningless to the string, cannot vary for
reproducible encryption. If a UTF-8 string is used for a password, the meflib routines
extract the terminal (most unique) bytes from each character to be used as the
password bytes. This can be done with the function extract_terminal_password_bytes();
it is not done in this function.
FUNCTION: AES_key_expansion()
// Prototype
void AES_key_expansion(ui1 *expanded_key, si1 *key);
Generates an expanded key from a key. A key is a 16 byte sequence. If, as is usually
the case, the key is a password, unused bytes should be zeroed, as these bytes, while
meaningless to the string, cannot vary for reproducible encryption / decryption. If a
UTF-8 string is used for a password, the meflib routines extract the terminal (most
unique) bytes from each character to be used as the password bytes. This can be done
with the function extract_terminal_password_bytes(); it is not done in this function.
/************************************************************************************/
/************************************ SHA Utilities *********************************/
/************************************************************************************/
// Function Prototypes
SHA-256 is the 256-bit version of the SHA-2 cryptographic hash function. Only the 256-
bit version is included in the library. Not all of the SHA functions are used by other
functions in the library, but are used by other SHA functions, and thus require inclusion.
Only those functions that are currently used in other (non-SHA) meflib functions are
described in this section.
FUNCTION: SHA256_initialize_h0_table()
// Prototype
ui4 *SHA256_initialize_h0_table(si4 global_flag);
Allocates and initializes SHA AES h0 table into heap space. If global_flag is set, the
MEF_globals pointer SHA_h0_table is also set to this value. This function is called by
initialize_meflib().
FUNCTION: SHA256_initialize_k_table()
// Prototype
ui4 *SHA256_initialize_k_table(si4 global_flag);
Allocates and initializes SHA AES k table into heap space. If global_flag is set, the
MEF_globals pointer SHA_k_table is also set to this value. This function is called by
initialize_meflib().
FUNCTION: sha256()
// Prototype
void sha256(const ui1 *message, ui4 len, ui1 *digest);
// Constant
#define SHA256_OUTPUT_SIZE 256
Returns a 256 byte SHA-2 hash of the message (of length len) in digest. This function is
used by process_password_data().
Mefrec API
User defined records are defined and coded in “mefrec.c” and “mefrec.h”. The functions
required for adding a new record type are described here. Record types themselves are
described in the file “MEF 3 Records Specification”.
Structures within records should have all members aligned to their type and the total
size evenly divisible by 8 (for 64-bit CPUs).
Records are named with 4 ascii characters and have a major and minor version
associated with them so that they can evolve, as needed, with time. These 4 characters
also define a type code as the bytes of a 4 byte unsigned integer. Note that translation
of ascii to hexadecimal on little endian machines requires reversing the byte
ordering the hexadecimal representation.
Each new record type should have two associated functions: a “show” function, and an
“alignment” function. “Show” functions display the contents of the records and have the
following form:
Name: show_mefrec_xxxx_type()
where “xxxx” is the record type name.
The “show” function should handle all versions of the record type. An example “show
function is shown below for the “Note” record type.
// Version 1.0
if (record_header->version_major == 1 && record_header->version_minor == 0) {
Note = (si1 *) record_header + MEFREC_Note_1_0_TEXT_OFFSET;
UTF8_printf("Note text: %s\n", Note);
}
// Unrecognized record version
else {
printf("Unrecognized Note version\n");
}
return;
}
All show function constants are defined in “mefrec.h”. The function show_record()
defined in mefrec.c must be modified in the switch statement, copied below, to add new
record types.
switch (type_code) {
case MEFREC_Note_TYPE_CODE:
show_mefrec_Note_type(record_header);
break;
case MEFREC_Seiz_TYPE_CODE:
show_mefrec_Seiz_type(record_header);
break;
case MEFREC_SyLg_TYPE_CODE:
show_mefrec_SyLg_type(record_header);
break;
case MEFREC_UnRc_TYPE_CODE:
default:
printf("\"%s\" (0x%x) is an unrecognized record type\n", \
record_header>type_string, type_code);
break;
}
Name: check_mefrec_xxxx_type_alignment()
where “xxxx” is the record type name.
New record “alignment” functions check the alignment of any structures represented in
the record body. Those structures are defined in “mefrec.h”. The function
check_record_structure_alignments() defined in mefrec.c must be modified in the serial
if statements, copied below, to add a new record type.
if ((check_mefrec_Note_type_alignment(bytes)) == MEF_FALSE)
return_value = MEF_FALSE;
if ((check_mefrec_Seiz_type_alignment(bytes)) == MEF_FALSE)
return_value = MEF_FALSE;
if ((check_mefrec_SyLg_type_alignment(bytes)) == MEF_FALSE)
return_value = MEF_FALSE;