/*
 * Miscellaneous useful functions.
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the MPEG TS, PS and ES tools.
 *
 * The Initial Developer of the Original Code is Amino Communications Ltd.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Amino Communications Ltd, Swavesey, Cambridge UK
 *
 * ***** END LICENSE BLOCK *****
 */
#ifndef _misc_fns
#define _misc_fns

#include "misc_defns.h"
#include "es_defns.h"
#ifndef _WIN32
#include <stdint.h>
#endif

#ifdef _WIN32
#include "compat.h"
#include <winsock2.h>
#endif // _WIN32

#define CRC32_POLY 0x04c11db7L

/*
 * Compute CRC32 over a block of data, by table method.
 *
 * Returns a working value, suitable for re-input for further blocks
 *
 * Notes: Input value should be 0xffffffff for the first block,
 *        else return value from previous call (not sure if that
 *        needs complementing before being passed back in).
 */
extern uint32_t crc32_block(uint32_t crc, byte *pData, int blk_len);

/*
 * Print out the bottom N bits from a byte
 */
extern void print_bits(int     num_bits,
                       byte    value);

/*
 * Print out (the first `max`) bytes of a byte array.
 *
 * - if `is_msg` then print as a message, otherwise as an error
 * - `name` is identifying text to start the report with.
 * - `data` is the byte data to print. This may be NULL.
 * - `length` is its length
 * - `max` is the maximum number of bytes to print
 *
 * Prints out::
 *
 *    <name> (<length>): b1 b2 b3 b4 ...
 *
 * where no more than `max` bytes are to be printed (and "..." is printed
 * if not all bytes were shown).
 */
extern void print_data(int   is_msg,
                       const char *name,
                       const byte  data[],
                       int   length,
                       int   max);
/*
 * Print out (the last `max`) bytes of a byte array.
 *
 * - `name` is identifying text to start the report with.
 * - `data` is the byte data to print. This may be NULL.
 * - `length` is its length
 * - `max` is the maximum number of bytes to print
 *
 * Prints out::
 *
 *    <name> (<length>): ... b1 b2 b3 b4
 *
 * where no more than `max` bytes are to be printed (and "..." is printed
 * if not all bytes were shown).
 */
extern void print_end_of_data(char *name,
                              byte  data[],
                              int   length,
                              int   max);
/*
 * Calculate log2 of `x` - for some reason this is missing from <math.h>
 */
extern double log2(double x);


// ============================================================
// Simple file I/O utilities
// ============================================================
/*
 * Read a given number of bytes from a file.
 *
 * This is a jacket for `read`, allowing for the future possibility of
 * buffered input, and simplifying error handling.
 *
 * - `input` is the file descriptor for the file
 * - `num_bytes` is how many bytes to read
 * - `data` is the buffer to read the bytes into
 *
 * Returns 0 if all goes well, EOF if end of file was read, or 1 if some
 * other error occurred (in which case it will already have output a message
 * on stderr about the problem).
 */
extern int read_bytes(int    input,
                      int    num_bytes,
                      byte  *data);
/*
 * Utility function to seek within a file
 *
 * - `filedes` is the file to seek within
 * - `posn` is the position to which to seek
 *
 * This is a jacket for::
 *
 *    new_posn = lseek(filedes,posn,SEEK_SET);
 *
 * Returns 0 if all went well, 1 if the seek failed (either because
 * it returned -1, or because the position reached was not the position
 * requested). If an error occurs, then an explanatory message will
 * already have been written to stderr.
 */
extern int seek_file(int       filedes,
                     offset_t  posn);
/*
 * Utility function to report the current location within a file
 *
 * - `filedes` is the file to seek within
 *
 * This is a jacket for::
 *
 *    posn = lseek(filedes,0,SEEK_CUR);
 *
 * Returns the current position in the file if all went well, otherwise
 * -1 (in which case an error message will already have been written
 * on stderr)
 */
extern offset_t tell_file(int    filedes);
/*
 * Utility function to open a file (descriptor), and report any errors
 *
 * This is intended only for very simple usage, and is not mean to be
 * a general purpose "open" replacement.
 *
 * - `filename` is the name of the file to open
 * - `for_write` should be TRUE if the file is to be written to,
 *   in which case it will be opened with flags O_WRONLY|O_CREAT|O_TRUNC,
 *   or FALSE if the file is to be read, in which case it will be
 *   opened with flag O_RDONLY. In both cases, on Windows the flag
 *   O_BINARY will also be set.
 *
 * Returns the file descriptor for the file, or -1 if it failed to open
 * the file.
 */
extern int open_binary_file(char  *filename,
                            int    for_write);
/*
 * Utility function to close a file (descriptor), and report any errors
 *
 * Returns 0 if all went well, 1 if an error occurred.
 */
extern int close_file(int  filedes);

// ============================================================
// More complex file I/O utilities
// ============================================================
/*
 * Open an input file appropriately for reading as ES.
 *
 * - `name` is the name of the file, or NULL if standard input
 *   is to be read from (which is not allowed if `use_pes` is
 *   TRUE).
 *
 * - If `use_pes` is true then the input file is PS or TS and should
 *   be read via a PES reader.
 *
 * - If `quiet` is true then information about the file being read will
 *   not be written out. Otherwise, its name and what is decided about
 *   its content will be printed.
 *
 * - If `force_stream_type` is true, then the caller asserts that
 *   the input shall be read according to `want_data`, and not whatever
 *   might be deduced from looking at the file itself.
 *
 * - If `force_stream_type` is true, then `want_data` should be one of
 *   VIDEO_H262, VIDEO_H264 or VIDEO_AVS. `is_data` will then be
 *   returned with the same value.
 *
 * - If `force_stream_type` is false, then the function will attempt
 *   to determine what type of data it has, and `is_data` will be set
 *   to whatever is determined (presumably one of VIDEO_H262, VIDEO_H264
 *   or VIDEO_AVS).
 *
 * - If input is from standard input, and `force_stream_type` is FALSE,
 *   `is_data` will always be set to VIDEO_H262, which may be incorrect.
 *
 * - `es` is the new ES reader context.
 *
 * Returns 0 if all goes well, 1 if something goes wrong. In the latter case,
 * suitable messages will have been written out to standard error.
 */
extern int open_input_as_ES(char   *name,
                            int     use_pes,
                            int     quiet,
                            int     force_stream_type,
                            int     want_data,
                            int    *is_data,
                            ES_p   *es);
/*
 * Close an input ES stream opened with `open_input_as_ES`.
 *
 * Specifically, this will close the ES stream and also any underlying PES
 * reader and file (unless the input was standard input).
 *
 * - `name` is the name of the file, used for error reporting.
 * - `es` is the ES stream to close. This will be set to NULL.
 *
 * Returns 0 if all goes well, 1 if something goes wrong. In the latter case,
 * suitable messages will have been written out to standard error.
 */
extern int close_input_as_ES(char   *name,
                             ES_p   *es);


// ============================================================
// Command line "helpers"
// ============================================================
/*
 * Read in an unsigned integer value, checking for extraneous characters.
 *
 * - `prefix` is an optional prefix for error messages, typically the
 *   name of the program. It may be NULL.
 * - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
 *   which is used in error messages.
 * - `str` is the string to read (typically ``argv[ii+1]``).
 * - `base` is the base to read to. If it is 0, then the user can use
 *   C-style expressions like "0x68" to specify the base on the command line.
 * - `value` is the value read.
 *
 * Returns 0 if all went well, 1 otherwise (in which case a message
 * explaining will have been written to stderr).
 */
extern int unsigned_value(char      *prefix,
                          char      *cmd,
                          char      *arg,
                          int        base,
                          uint32_t  *value);
/*
 * Read in an integer value, checking for extraneous characters.
 *
 * - `prefix` is an optional prefix for error messages, typically the
 *   name of the program. It may be NULL.
 * - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
 *   which is used in error messages.
 * - `str` is the string to read (typically ``argv[ii+1]``).
 * - if `positive` is true, then the number read must be positive (0 or more).
 * - `base` is the base to read to. If it is 0, then the user can use
 *   C-style expressions like "0x68" to specify the base on the command line.
 * - `value` is the value read.
 *
 * Returns 0 if all went well, 1 otherwise (in which case a message
 * explaining will have been written to stderr).
 */
extern int int_value(char *prefix,
                     char *cmd,
                     char *str,
                     int   positive,
                     int   base,
                     int  *value);
/*
 * Read in an integer value, checking for extraneous characters and a range.
 *
 * - `prefix` is an optional prefix for error messages, typically the
 *   name of the program. It may be NULL.
 * - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
 *   which is used in error messages.
 * - `str` is the string to read (typically ``argv[ii+1]``).
 * - `minimum` is the minimum value allowed.
 * - `maximum` is the maximum value allowed.
 * - `base` is the base to read to. If it is 0, then the user can use
 *   C-style expressions like "0x68" to specify the base on the command line.
 * - `value` is the value read.
 *
 * Returns 0 if all went well, 1 otherwise (in which case a message
 * explaining will have been written to stderr).
 */
extern int int_value_in_range(char *prefix,
                              char *cmd,
                              char *arg,
                              int   minimum,
                              int   maximum,
                              int   base,
                              int  *value);
/*
 * Read in a double value, checking for extraneous characters.
 *
 * - `prefix` is an optional prefix for error messages, typically the
 *   name of the program. It may be NULL.
 * - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
 *   which is used in error messages.
 * - `str` is the string to read (typically ``argv[ii+1]``).
 * - if `positive` is true, then the number read must be positive (0 or more).
 * - `value` is the value read.
 *
 * Returns 0 if all went well, 1 otherwise (in which case a message
 * explaining will have been written to stderr).
 */
extern int double_value(char   *prefix,
                           char   *cmd,
                           char   *arg,
                           int     positive,
                           double *value);
/*
 * Read in a hostname and (optional) port
 *
 * - `prefix` is an optional prefix for error messages, typically the
 *   name of the program. It may be NULL.
 * - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
 *   which is used in error messages.
 * - `arg` is the string to read (typically ``argv[ii+1]``).
 * - `hostname` is the host name read
 * - `port` is the port read (note that this is not touched if there is
 *   no port number, so it may be set to a default before calling this
 *   function)
 *
 * Note that this works by pointing `hostname` to the start of the `arg`
 * string, and then if there is a ':' in `arg`, changing that colon to
 * a '\0' delimiter, and interpreting the string thereafter as the port
 * number. If *that* fails, it resets the '\0' as a ':'.
 *
 * Returns 0 if all went well, 1 otherwise (in which case a message
 * explaining will have been written to stderr).
 */
extern int host_value(char  *prefix,
                      char  *cmd,
                      char  *arg,
                      char **hostname,
                      int   *port);




// ============================================================
// Sockets
// ============================================================
#ifdef _WIN32
/*
 * Start up WINSOCK so we can use sockets.
 *
 * Note that each successful call of this *must* be matched by a call
 * of winsock_cleanup().
 *
 * Returns 0 if it works, 1 if it fails.
 */
extern int winsock_startup(void);
/*
 * Convert a WinSock error number into a string and print it out on stderr
 */
extern void print_winsock_err(int err);
#endif // _WIN32
/*
 * Connect to a socket, to allow us to write to it, using TCP/IP.
 *
 * - `hostname` is the name of the host to connect to
 * - `port` is the port to use
 * - if `use_tcpip`, then a TCP/IP connection will be made, otherwise UDP.
 *   For UDP, multicast TTL will be enabled.
 * - If the destination address (`hostname`) is multicast and `multicast_ifaddr`
 *   is supplied, it is used to select (by IP address) the network interface
 *   on which to send the multicasts.  It may be NULL to use the default,
 *   or for non-multicast cases.
 *
 * A socket connected to via this function must be disconnected from with
 * disconnect_socket().
 *
 * Returns a positive integer (the file descriptor for the socket) if it
 * succeeds, or -1 if it fails, in which case it will have complained on
 * stderr.
 */
extern int connect_socket(char *hostname,
                          int   port,
                          int   use_tcpip,
                          char *multicast_ifaddr);

/*
 * Disconnect from a socket (close it).
 *
 * Returns 0 if all goes well, 1 otherwise.
 */
#ifdef _WIN32
extern int disconnect_socket(SOCKET  socket);
#else  // _WIN32
extern int disconnect_socket(int  socket);
#endif // _WIN32

/*! Decode a host byte order address to a static buffer. */
const char *ipv4_addr_to_string(const uint32_t addr);

/*! Decode a string to a host byte order address */
int ipv4_string_to_addr(uint32_t *dest, const char *addr);


// ============================================================
// Byte order handling
// ============================================================


static inline uint32_t uint_32_be(const uint8_t *const p)
{
  return (((int)p[0]&0xff) << 24) | 
    (((int)p[1]&0xff) << 16) | 
    (((int)p[2]&0xff) << 8) |
    (((int)p[3]&0xff));
}


static inline uint32_t uint_32_le(const uint8_t *const p)
{
  return (((int)p[0]&0xff) | 
    (((int)p[1]&0xff) << 8) |
    (((int)p[2]&0xff) << 16) |
    (((int)p[3]&0xff) << 24));
}


static inline uint16_t uint_16_be(const uint8_t *const p)
{
  return ((((int)p[0])&0xff)<<8) | 
    ((((int)p[1])&0xff));
}

static inline uint16_t uint_16_le(const uint8_t *const p)
{
  return (((int)p[0]&0xff) | 
	  ((int)p[1]&0xff)<<8);
}


// ============================================================
// Time diffs
// ============================================================

#define PCR_UNSIGNED_WRAP (300ULL * (1ULL << 33))

// (x - y) with allowance for PCR wrap - unsigned version
static inline uint64_t
pcr_unsigned_diff(uint64_t x, uint64_t y)
{
  return x > y ? x - y :
    300ULL * (1ULL << 33) - (y - x);
}

#define PCR_SIGNED_WRAP (300LL * (1LL << 33))
#define PCR_SIGNED_MAX (PCR_SIGNED_WRAP / 2LL - 1LL)
#define PCR_SIGNED_MIN (-PCR_SIGNED_WRAP / 2LL)

// (x - y) with allowance for PCR wrap - signed version
static inline int64_t
pcr_signed_diff(uint64_t x, uint64_t y)
{
  int64_t r = x - y;

  return r < PCR_SIGNED_MIN ? r + PCR_SIGNED_WRAP :
    r > PCR_SIGNED_MAX ? r - PCR_SIGNED_WRAP :
      r;
}

// Deal with simple overflow
static inline uint64_t
pcr_unsigned_wrap(uint64_t x)
{
  return x >= PCR_UNSIGNED_WRAP ? x - PCR_UNSIGNED_WRAP : x;
}

static inline int64_t
pts_signed_diff(uint64_t x, uint64_t y)
{
  int64_t r = x - y;
  return (r << 31) >> 31;
}




#endif // _misc_fns

// Local Variables:
// tab-width: 8
// indent-tabs-mode: nil
// c-basic-offset: 2
// End:
// vim: set tabstop=8 shiftwidth=2 expandtab:
