Blame src/bs_size.c

Packit Service af52df
#include <gmp.h>
Packit Service af52df
#include <mpfr.h>
Packit Service af52df
#include <langinfo.h>
Packit Service af52df
#include <stdarg.h>
Packit Service af52df
#include <stdio.h>
Packit Service af52df
#include <inttypes.h>
Packit Service af52df
#include <string.h>
Packit Service af52df
#include <ctype.h>
Packit Service af52df
#include <limits.h>
Packit Service af52df
#include <pcre.h>
Packit Service af52df
Packit Service af52df
#include "bs_size.h"
Packit Service af52df
#include "gettext.h"
Packit Service af52df
Packit Service af52df
#define  _(String) gettext (String)
Packit Service af52df
#define N_(String) String
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * SECTION: bs_size
Packit Service af52df
 * @title: BSSize
Packit Service af52df
 * @short_description: a class facilitating work with sizes in bytes
Packit Service af52df
 * @include: bs_size.h
Packit Service af52df
 *
Packit Service af52df
 * #BSSize is a type that facilitates work with sizes in bytes by providing
Packit Service af52df
 * functions/methods that are required for parsing users input when entering
Packit Service af52df
 * size, showing size in nice human-readable format, storing sizes bigger than
Packit Service af52df
 * %UINT64_MAX and doing calculations with sizes without loss of
Packit Service af52df
 * precision/information. The class is able to hold negative sizes and do
Packit Service af52df
 * operations on/with them, but some of the (division and multiplication)
Packit Service af52df
 * operations simply ignore the signs of the operands (check the documentation).
Packit Service af52df
 *
Packit Service af52df
 * The reason why some functions take or return a float as a string instead of a
Packit Service af52df
 * float directly is because a string "0.3" can be translated into 0.3 with
Packit Service af52df
 * appropriate precision while 0.3 as float is probably something like
Packit Service af52df
 * 0.294343... with unknown precision.
Packit Service af52df
 *
Packit Service af52df
 */
Packit Service af52df
Packit Service af52df
Packit Service af52df
/***************
Packit Service af52df
 * STATIC DATA *
Packit Service af52df
 ***************/
Packit Service af52df
static char const * const b_units[BS_BUNIT_UNDEF] = {
Packit Service af52df
    /* TRANSLATORS: 'B' for bytes */
Packit Service af52df
    N_("B"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for kibibyte, 2**10 bytes */
Packit Service af52df
    N_("KiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for mebibyte, 2**20 bytes */
Packit Service af52df
    N_("MiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for gibibyte, 2**30 bytes */
Packit Service af52df
    N_("GiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for tebibyte, 2**40 bytes */
Packit Service af52df
    N_("TiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for pebibyte, 2**50 bytes */
Packit Service af52df
    N_("PiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for exbibyte, 2**60 bytes */
Packit Service af52df
    N_("EiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for zebibyte, 2**70 bytes */
Packit Service af52df
    N_("ZiB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for yobibyte, 2**80 bytes */
Packit Service af52df
    N_("YiB")
Packit Service af52df
};
Packit Service af52df
Packit Service af52df
static char const * const d_units[BS_DUNIT_UNDEF] = {
Packit Service af52df
    /* TRANSLATORS: 'B' for bytes */
Packit Service af52df
    N_("B"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for kilobyte, 10**3 bytes */
Packit Service af52df
    N_("KB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for megabyte, 10**6 bytes */
Packit Service af52df
    N_("MB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for gigabyte, 10**9 bytes */
Packit Service af52df
    N_("GB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for terabyte, 10**12 bytes */
Packit Service af52df
    N_("TB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for petabyte, 10**15 bytes */
Packit Service af52df
    N_("PB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for exabyte, 10**18 bytes */
Packit Service af52df
    N_("EB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for zettabyte, 10**21 bytes */
Packit Service af52df
    N_("ZB"),
Packit Service af52df
    /* TRANSLATORS: abbreviation for yottabyte, 10**24 bytes */
Packit Service af52df
    N_("YB")
Packit Service af52df
};
Packit Service af52df
Packit Service af52df
Packit Service af52df
/****************************
Packit Service af52df
 * STRUCT DEFINITIONS       *
Packit Service af52df
 ****************************/
Packit Service af52df
/**
Packit Service af52df
 * BSSize:
Packit Service af52df
 *
Packit Service af52df
 * The BSSize struct contains only private fields and should not be directly
Packit Service af52df
 * accessed.
Packit Service af52df
 */
Packit Service af52df
struct _BSSize {
Packit Service af52df
    mpz_t bytes;
Packit Service af52df
};
Packit Service af52df
Packit Service af52df
Packit Service af52df
/********************
Packit Service af52df
 * HELPER FUNCTIONS *
Packit Service af52df
 ********************/
Packit Service af52df
static void bs_size_init (BSSize size) {
Packit Service af52df
    /* let's start with 64 bits of space */
Packit Service af52df
    mpz_init2 (size->bytes, (mp_bitcnt_t) 64);
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
static char *strdup_printf (const char *fmt, ...) {
Packit Service af52df
    int num = 0;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
    va_list ap;
Packit Service af52df
    va_start (ap, fmt);
Packit Service af52df
    num = vasprintf (&ret, fmt, ap);
Packit Service af52df
    va_end (ap);
Packit Service af52df
    if (num <= 0)
Packit Service af52df
        /* make sure we return NULL on error */
Packit Service af52df
        ret = NULL;
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * replace_char_with_str: (skip)
Packit Service af52df
 *
Packit Service af52df
 * Replaces all apperances of @char in @str with @new.
Packit Service af52df
 */
Packit Service af52df
static char *replace_char_with_str (const char *str, char orig, const char *new) {
Packit Service af52df
    uint64_t offset = 0;
Packit Service af52df
    uint64_t i = 0;
Packit Service af52df
    uint64_t j = 0;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
    const char *next = NULL;
Packit Service af52df
    uint64_t count = 0;
Packit Service af52df
Packit Service af52df
    if (!str)
Packit Service af52df
        return NULL;
Packit Service af52df
Packit Service af52df
    next = str;
Packit Service af52df
    for (next=strchr (next, orig); next; count++, next=strchr (++next, orig));
Packit Service af52df
Packit Service af52df
    /* allocate space for the string [strlen(str)] with the char replaced by the
Packit Service af52df
       string [strlen(new) - 1] $count times and a \0 byte at the end [ + 1] */
Packit Service af52df
    ret = malloc (sizeof(char) * (strlen(str) + (strlen(new) - 1) * count + 1));
Packit Service af52df
Packit Service af52df
    for (i=0; str[i]; i++) {
Packit Service af52df
        if (str[i] == orig)
Packit Service af52df
            for (j=0; new[j]; j++) {
Packit Service af52df
                ret[i+offset] = new[j];
Packit Service af52df
                if (new[j+1])
Packit Service af52df
                    /* something more to copy over */
Packit Service af52df
                    offset++;
Packit Service af52df
            }
Packit Service af52df
        else
Packit Service af52df
            ret[i+offset] = str[i];
Packit Service af52df
    }
Packit Service af52df
    ret[i+offset] = '\0';
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * replace_str_with_str: (skip)
Packit Service af52df
 *
Packit Service af52df
 * Replaces the first appearance of @orig in @str with @new.
Packit Service af52df
 */
Packit Service af52df
static char *replace_str_with_str (const char *str, const char *orig, const char *new) {
Packit Service af52df
    char *pos = NULL;
Packit Service af52df
    int str_len = 0;
Packit Service af52df
    int orig_len = 0;
Packit Service af52df
    int new_len = 0;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
    int ret_size = 0;
Packit Service af52df
    char *dest = NULL;
Packit Service af52df
Packit Service af52df
    pos = strstr (str, orig);
Packit Service af52df
    if (!pos)
Packit Service af52df
        /* nothing to do, just return a copy */
Packit Service af52df
        return strdup (str);
Packit Service af52df
Packit Service af52df
    str_len = strlen (str);
Packit Service af52df
    orig_len = strlen (orig);
Packit Service af52df
    new_len = strlen (new);
Packit Service af52df
    ret_size = str_len + new_len - orig_len + 1;
Packit Service af52df
    ret = malloc (sizeof(char) * ret_size);
Packit Service af52df
    memset (ret, 0, ret_size);
Packit Service af52df
    memcpy (ret, str, pos - str);
Packit Service af52df
    dest = ret + (pos - str);
Packit Service af52df
    memcpy (dest, new, new_len);
Packit Service af52df
    dest = dest + new_len;
Packit Service af52df
    memcpy (dest, pos + orig_len, str_len - (pos - str) - orig_len);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * strstrip: (skip)
Packit Service af52df
 *
Packit Service af52df
 * Strips leading and trailing whitespace from the string (**IN-PLACE**)
Packit Service af52df
 */
Packit Service af52df
static void strstrip(char *str) {
Packit Service af52df
    int i = 0;
Packit Service af52df
    int begin = 0;
Packit Service af52df
    int end = strlen(str) - 1;
Packit Service af52df
Packit Service af52df
    while (isspace(str[begin]))
Packit Service af52df
        begin++;
Packit Service af52df
    while ((end >= begin) && isspace(str[end]))
Packit Service af52df
        end--;
Packit Service af52df
Packit Service af52df
    for (i=begin; i <= end; i++)
Packit Service af52df
        str[i - begin] = str[i];
Packit Service af52df
Packit Service af52df
    str[i-begin] = '\0';
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
static bool multiply_size_by_unit (mpfr_t size, char *unit_str) {
Packit Service af52df
    BSBunit bunit = BS_BUNIT_UNDEF;
Packit Service af52df
    BSDunit dunit = BS_DUNIT_UNDEF;
Packit Service af52df
    uint64_t pwr = 0;
Packit Service af52df
    mpfr_t dec_mul;
Packit Service af52df
    size_t unit_str_len = 0;
Packit Service af52df
Packit Service af52df
    unit_str_len = strlen (unit_str);
Packit Service af52df
Packit Service af52df
    for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++)
Packit Service af52df
        if (strncasecmp (unit_str, b_units[bunit-BS_BUNIT_B], unit_str_len) == 0) {
Packit Service af52df
            pwr = (uint64_t) bunit - BS_BUNIT_B;
Packit Service af52df
            mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN);
Packit Service af52df
            return true;
Packit Service af52df
        }
Packit Service af52df
Packit Service af52df
    mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpfr_set_ui (dec_mul, 1000, MPFR_RNDN);
Packit Service af52df
    for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++)
Packit Service af52df
        if (strncasecmp (unit_str, d_units[dunit-BS_DUNIT_B], unit_str_len) == 0) {
Packit Service af52df
            pwr = (uint64_t) (dunit - BS_DUNIT_B);
Packit Service af52df
            mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN);
Packit Service af52df
            mpfr_mul (size, size, dec_mul, MPFR_RNDN);
Packit Service af52df
            mpfr_clear (dec_mul);
Packit Service af52df
            return true;
Packit Service af52df
        }
Packit Service af52df
Packit Service af52df
    /* not found among the binary and decimal units, let's try their translated
Packit Service af52df
       verions */
Packit Service af52df
    for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++)
Packit Service af52df
        if (strncasecmp (unit_str, _(b_units[bunit-BS_BUNIT_B]), unit_str_len) == 0) {
Packit Service af52df
            pwr = (uint64_t) bunit - BS_BUNIT_B;
Packit Service af52df
            mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN);
Packit Service af52df
            return true;
Packit Service af52df
        }
Packit Service af52df
Packit Service af52df
    mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpfr_set_ui (dec_mul, 1000, MPFR_RNDN);
Packit Service af52df
    for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++)
Packit Service af52df
        if (strncasecmp (unit_str, _(d_units[dunit-BS_DUNIT_B]), unit_str_len) == 0) {
Packit Service af52df
            pwr = (uint64_t) (dunit - BS_DUNIT_B);
Packit Service af52df
            mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN);
Packit Service af52df
            mpfr_mul (size, size, dec_mul, MPFR_RNDN);
Packit Service af52df
            mpfr_clear (dec_mul);
Packit Service af52df
            return true;
Packit Service af52df
        }
Packit Service af52df
Packit Service af52df
    return false;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * set_error: (skip)
Packit Service af52df
 *
Packit Service af52df
 * Sets @error to @code and @msg (if not %NULL). **TAKES OVER @msg.**
Packit Service af52df
 */
Packit Service af52df
static void set_error (BSError **error, BSErrorCode code, char *msg) {
Packit Service af52df
    *error = (BSError *) malloc (sizeof(BSError));
Packit Service af52df
    (*error)->code = code;
Packit Service af52df
    (*error)->msg = msg;
Packit Service af52df
    return;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
typedef void (*MpzOp) (mpz_t ROP, const mpz_t OP1, unsigned long int OP2);
Packit Service af52df
static void do_64bit_add_sub (MpzOp op, mpz_t rop, const mpz_t op1, uint64_t op2) {
Packit Service af52df
    uint64_t i = 0;
Packit Service af52df
    uint64_t div = 0;
Packit Service af52df
    uint64_t mod = 0;
Packit Service af52df
Packit Service af52df
    /* small enough to just work */
Packit Service af52df
    if (op2 < (uint64_t) ULONG_MAX) {
Packit Service af52df
        op (rop, op1, (unsigned long int) op2);
Packit Service af52df
        return;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpz_set (rop, op1);
Packit Service af52df
    div = op2 / (uint64_t) ULONG_MAX;
Packit Service af52df
    mod = op2 % (uint64_t) ULONG_MAX;
Packit Service af52df
    for (i=0; i < div; i++)
Packit Service af52df
        op (rop, rop, (unsigned long int) ULONG_MAX);
Packit Service af52df
    op (rop, rop, (unsigned long int) mod);
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
static void mul_64bit (mpz_t rop, const mpz_t op1, uint64_t op2) {
Packit Service af52df
    uint64_t i = 0;
Packit Service af52df
    uint64_t div = 0;
Packit Service af52df
    uint64_t mod = 0;
Packit Service af52df
    mpz_t aux;
Packit Service af52df
    mpz_t res;
Packit Service af52df
Packit Service af52df
    /* small enough to just work */
Packit Service af52df
    if (op2 < (uint64_t) ULONG_MAX) {
Packit Service af52df
        mpz_mul_ui (rop, op1, (unsigned long int) op2);
Packit Service af52df
        return;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpz_init2 (aux, (mp_bitcnt_t) 64);
Packit Service af52df
    mpz_init2 (res, (mp_bitcnt_t) 64);
Packit Service af52df
Packit Service af52df
    mpz_set_ui (res, 0);
Packit Service af52df
    div = op2 / (uint64_t) ULONG_MAX;
Packit Service af52df
    mod = op2 % (uint64_t) ULONG_MAX;
Packit Service af52df
    for (i=0; i < div; i++) {
Packit Service af52df
        mpz_mul_ui (aux, op1, (unsigned long int) ULONG_MAX);
Packit Service af52df
        mpz_add (res, res, aux);
Packit Service af52df
    }
Packit Service af52df
    mpz_mul_ui (aux, op1, (unsigned long int) mod);
Packit Service af52df
    mpz_add (res, res, aux);
Packit Service af52df
Packit Service af52df
    mpz_set (rop, res);
Packit Service af52df
    mpz_clear (aux);
Packit Service af52df
    mpz_clear (res);
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
Packit Service af52df
/***************
Packit Service af52df
 * DESTRUCTORS *
Packit Service af52df
 * *************/
Packit Service af52df
/**
Packit Service af52df
 * bs_size_free:
Packit Service af52df
 *
Packit Service af52df
 * Clears @size and frees the allocated resources.
Packit Service af52df
 */
Packit Service af52df
void bs_size_free (BSSize size) {
Packit Service af52df
    if (size) {
Packit Service af52df
        mpz_clear (size->bytes);
Packit Service af52df
        free (size);
Packit Service af52df
    }
Packit Service af52df
    return;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_clear_error:
Packit Service af52df
 *
Packit Service af52df
 * Clears @error and frees the allocated resources.
Packit Service af52df
 */
Packit Service af52df
void bs_clear_error (BSError **error) {
Packit Service af52df
    if (error && *error) {
Packit Service af52df
        free ((*error)->msg);
Packit Service af52df
        free (*error);
Packit Service af52df
        *error = NULL;
Packit Service af52df
    }
Packit Service af52df
    return;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/****************
Packit Service af52df
 * CONSTRUCTORS *
Packit Service af52df
 ****************/
Packit Service af52df
/**
Packit Service af52df
 * bs_size_new: (constructor)
Packit Service af52df
 *
Packit Service af52df
 * Creates a new #BSSize instance initialized to 0.
Packit Service af52df
 *
Packit Service af52df
 * Returns: a new #BSSize initialized to 0.
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_new (void) {
Packit Service af52df
    BSSize ret = (BSSize) malloc (sizeof(struct _BSSize));
Packit Service af52df
    bs_size_init (ret);
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_new_from_bytes: (constructor)
Packit Service af52df
 * @bytes: number of bytes
Packit Service af52df
 * @sgn: sign of the size -- if being -1, the size is initialized to
Packit Service af52df
 *       -@bytes, other values are ignored
Packit Service af52df
 *
Packit Service af52df
 * Creates a new #BSSize instance.
Packit Service af52df
 *
Packit Service af52df
 * Returns: a new #BSSize
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_new_from_bytes (uint64_t bytes, int sgn) {
Packit Service af52df
    char *num_str = NULL;
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    int ok = 0;
Packit Service af52df
Packit Service af52df
    ok = asprintf (&num_str, "%"PRIu64, bytes);
Packit Service af52df
    if (ok == -1)
Packit Service af52df
        /* probably cannot allocate memory, there's nothing more we can do */
Packit Service af52df
        return ret;
Packit Service af52df
    mpz_set_str (ret->bytes, num_str, 10);
Packit Service af52df
    free (num_str);
Packit Service af52df
    if (sgn == -1)
Packit Service af52df
        mpz_neg (ret->bytes, ret->bytes);
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_new_from_str: (constructor)
Packit Service af52df
 * @size_str: string representing the size as a number and an optional unit
Packit Service af52df
 *            (e.g. "1 GiB")
Packit Service af52df
 *
Packit Service af52df
 * Creates a new #BSSize instance.
Packit Service af52df
 *
Packit Service af52df
 * Returns: a new #BSSize
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
Packit Service af52df
    char const * const pattern = "^\\s*         # white space \n" \
Packit Service af52df
                                  "(?P<numeric>  # the numeric part consists of three parts, below \n" \
Packit Service af52df
                                  " (-|\\+)?     # optional sign character \n" \
Packit Service af52df
                                  " (?P<base>([0-9\\.%s]+))       # base \n" \
Packit Service af52df
                                  " (?P<exp>(e|E)(-|\\+)[0-9]+)?) # exponent \n" \
Packit Service af52df
                                  "\\s*               # white space \n" \
Packit Service af52df
                                  "(?P<rest>[^\\s]*)\\s*$ # unit specification";
Packit Service af52df
    char *real_pattern = NULL;
Packit Service af52df
    pcre *regex = NULL;
Packit Service af52df
    const char *error_msg = NULL;
Packit Service af52df
    int erroffset;
Packit Service af52df
    int str_len = 0;
Packit Service af52df
    int ovector[30];            /* should be a multiple of 3 */
Packit Service af52df
    int str_count = 0;
Packit Service af52df
    char *num_str = NULL;
Packit Service af52df
    const char *radix_char = NULL;
Packit Service af52df
    char *loc_size_str = NULL;
Packit Service af52df
    mpf_t parsed_size;
Packit Service af52df
    mpfr_t size;
Packit Service af52df
    int status = 0;
Packit Service af52df
    char *unit_str = NULL;
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
Packit Service af52df
    radix_char = nl_langinfo (RADIXCHAR);
Packit Service af52df
    if (strncmp (radix_char, ".", 1) != 0)
Packit Service af52df
        real_pattern = strdup_printf (pattern, radix_char);
Packit Service af52df
    else
Packit Service af52df
        real_pattern = strdup_printf (pattern, "");
Packit Service af52df
Packit Service af52df
    regex = pcre_compile (real_pattern, PCRE_EXTENDED, &error_msg, &erroffset, NULL);
Packit Service af52df
    free (real_pattern);
Packit Service af52df
    if (!regex) {
Packit Service af52df
        /* TODO: populate error */
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    loc_size_str = replace_char_with_str (size_str, '.', radix_char);
Packit Service af52df
    str_len = strlen (loc_size_str);
Packit Service af52df
Packit Service af52df
    str_count = pcre_exec (regex, NULL, loc_size_str, str_len,
Packit Service af52df
                           0, 0, ovector, 30);
Packit Service af52df
    if (str_count < 0) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
Packit Service af52df
        pcre_free (regex);
Packit Service af52df
        free (loc_size_str);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    status = pcre_get_named_substring (regex, loc_size_str, ovector, str_count, "numeric", (const char **)&num_str);
Packit Service af52df
    if (status < 0) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
Packit Service af52df
        pcre_free (regex);
Packit Service af52df
        free (loc_size_str);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    /* parse the number using GMP because it knows how to handle localization
Packit Service af52df
       much better than MPFR */
Packit Service af52df
    mpf_init2 (parsed_size, BS_FLOAT_PREC_BITS);
Packit Service af52df
    status = mpf_set_str (parsed_size, *num_str == '+' ? num_str+1 : num_str, 10);
Packit Service af52df
    free (num_str);
Packit Service af52df
    if (status != 0) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
Packit Service af52df
        pcre_free (regex);
Packit Service af52df
        free (loc_size_str);
Packit Service af52df
        mpf_clear (parsed_size);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
    /* but use MPFR from now on because GMP thinks 0.1*1000 = 99 */
Packit Service af52df
    mpfr_init2 (size, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpfr_set_f (size, parsed_size, MPFR_RNDN);
Packit Service af52df
    mpf_clear (parsed_size);
Packit Service af52df
Packit Service af52df
    status = pcre_get_named_substring (regex, loc_size_str, ovector, str_count, "rest", (const char **)&unit_str);
Packit Service af52df
    if ((status >= 0) && strncmp (unit_str, "", 1) != 0) {
Packit Service af52df
        strstrip (unit_str);
Packit Service af52df
        if (!multiply_size_by_unit (size, unit_str)) {
Packit Service af52df
            set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to recognize unit from the spec: %s", size_str));
Packit Service af52df
            free (unit_str);
Packit Service af52df
            pcre_free (regex);
Packit Service af52df
            free (loc_size_str);
Packit Service af52df
            mpfr_clear (size);
Packit Service af52df
            return NULL;
Packit Service af52df
        }
Packit Service af52df
    }
Packit Service af52df
    free (unit_str);
Packit Service af52df
    pcre_free (regex);
Packit Service af52df
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpfr_get_z (ret->bytes, size, MPFR_RNDZ);
Packit Service af52df
Packit Service af52df
    free (loc_size_str);
Packit Service af52df
    mpfr_clear (size);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_new_from_size: (constructor)
Packit Service af52df
 * @size: the size to create a new instance from (a copy of)
Packit Service af52df
 *
Packit Service af52df
 * Creates a new instance of #BSSize.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new #BSSize instance which is copy of @size.
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_new_from_size (const BSSize size) {
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpz_set (ret->bytes, size->bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/*****************
Packit Service af52df
 * QUERY METHODS *
Packit Service af52df
 *****************/
Packit Service af52df
/**
Packit Service af52df
 * bs_size_get_bytes:
Packit Service af52df
 * @sgn: (allow-none) (out): sign of the @size - -1, 0 or 1 for negative, zero or positive
Packit Service af52df
 *                           size respectively
Packit Service af52df
 *
Packit Service af52df
 * Get the number of bytes of the @size.
Packit Service af52df
 *
Packit Service af52df
 * Returns: the @size in a number of bytes
Packit Service af52df
 */
Packit Service af52df
uint64_t bs_size_get_bytes (const BSSize size, int *sgn, BSError **error) {
Packit Service af52df
    char *num_str = NULL;
Packit Service af52df
    mpz_t max;
Packit Service af52df
    uint64_t ret = 0;
Packit Service af52df
    int ok = 0;
Packit Service af52df
Packit Service af52df
    mpz_init2 (max, (mp_bitcnt_t) 64);
Packit Service af52df
    ok = asprintf (&num_str, "%"PRIu64, UINT64_MAX);
Packit Service af52df
    if (ok == -1) {
Packit Service af52df
        /* we probably cannot allocate memory so we are doomed */
Packit Service af52df
        set_error (error, BS_ERROR_FAIL, strdup("Failed to allocate memory"));
Packit Service af52df
        mpz_clear (max);
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
    mpz_set_str (max, num_str, 10);
Packit Service af52df
    free (num_str);
Packit Service af52df
    if (mpz_cmp (size->bytes, max) > 0) {
Packit Service af52df
        set_error (error, BS_ERROR_OVER, strdup("The size is too big, cannot be returned as a 64bit number of bytes"));
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
    mpz_clear (max);
Packit Service af52df
    if (sgn)
Packit Service af52df
        *sgn = mpz_sgn (size->bytes);
Packit Service af52df
    if (mpz_cmp_ui (size->bytes, UINT64_MAX) <= 0)
Packit Service af52df
        return (uint64_t) mpz_get_ui (size->bytes);
Packit Service af52df
    else {
Packit Service af52df
        num_str = bs_size_get_bytes_str (size);
Packit Service af52df
        ret = strtoull (num_str, NULL, 10);
Packit Service af52df
        free (num_str);
Packit Service af52df
        return ret;
Packit Service af52df
    }
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_sgn:
Packit Service af52df
 *
Packit Service af52df
 * Get the sign of the size.
Packit Service af52df
 *
Packit Service af52df
 * Returns: -1, 0 or 1 if @size is negative, zero or positive, respectively
Packit Service af52df
 */
Packit Service af52df
int bs_size_sgn (const BSSize size) {
Packit Service af52df
    return mpz_sgn (size->bytes);
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_get_bytes_str:
Packit Service af52df
 *
Packit Service af52df
 * Get the number of bytes in @size as a string. This way, the caller doesn't
Packit Service af52df
 * have to care about the limitations of some particular integer type.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): the string representing the @size as a number of bytes.
Packit Service af52df
 */
Packit Service af52df
char* bs_size_get_bytes_str (const BSSize size) {
Packit Service af52df
    return mpz_get_str (NULL, 10, size->bytes);
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_convert_to:
Packit Service af52df
 * @unit: the unit to convert @size to
Packit Service af52df
 *
Packit Service af52df
 * Get the @size converted to @unit as a string representing a floating-point
Packit Service af52df
 * number.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a string representing the floating-point number
Packit Service af52df
 *                           that equals to @size converted to @unit
Packit Service af52df
 */
Packit Service af52df
char* bs_size_convert_to (const BSSize size, BSUnit unit, BSError **error) {
Packit Service af52df
    BSBunit b_unit = BS_BUNIT_B;
Packit Service af52df
    BSDunit d_unit = BS_DUNIT_B;
Packit Service af52df
    mpf_t divisor;
Packit Service af52df
    mpf_t result;
Packit Service af52df
    bool found_match = false;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
Packit Service af52df
    mpf_init2 (divisor, BS_FLOAT_PREC_BITS);
Packit Service af52df
    for (b_unit = BS_BUNIT_B; !found_match && b_unit != BS_BUNIT_UNDEF; b_unit++) {
Packit Service af52df
        if (unit.bunit == b_unit) {
Packit Service af52df
            found_match = true;
Packit Service af52df
            mpf_set_ui (divisor, 1);
Packit Service af52df
            mpf_mul_2exp (divisor, divisor, 10 * (b_unit - BS_BUNIT_B));
Packit Service af52df
        }
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    for (d_unit = BS_DUNIT_B; !found_match && d_unit != BS_DUNIT_UNDEF; d_unit++) {
Packit Service af52df
        if (unit.dunit == d_unit) {
Packit Service af52df
            found_match = true;
Packit Service af52df
            mpf_set_ui (divisor, 1000);
Packit Service af52df
            mpf_pow_ui (divisor, divisor, (d_unit - BS_DUNIT_B));
Packit Service af52df
        }
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    if (!found_match) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, "Invalid unit spec given");
Packit Service af52df
        mpf_clear (divisor);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpf_init2 (result, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_set_z (result, size->bytes);
Packit Service af52df
Packit Service af52df
    mpf_div (result, result, divisor);
Packit Service af52df
Packit Service af52df
    gmp_asprintf (&ret, "%.*Fg", BS_FLOAT_PREC_BITS/3, result);
Packit Service af52df
    mpf_clears (divisor, result, NULL);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_human_readable:
Packit Service af52df
 * @min_unit: the smallest unit the returned representation should use
Packit Service af52df
 * @max_places: maximum number of decimal places the representation should use
Packit Service af52df
 * @xlate: whether to try to translate the representation or not
Packit Service af52df
 *
Packit Service af52df
 * Get a human-readable representation of @size.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a string which is human-readable representation of
Packit Service af52df
 *                           @size according to the restrictions given by the
Packit Service af52df
 *                           other parameters
Packit Service af52df
 */
Packit Service af52df
char* bs_size_human_readable (const BSSize size, BSBunit min_unit, int max_places, bool xlate) {
Packit Service af52df
    mpf_t cur_val;
Packit Service af52df
    char *num_str = NULL;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
    int len = 0;
Packit Service af52df
    char *zero = NULL;
Packit Service af52df
    char *radix_char = NULL;
Packit Service af52df
    int sign = 0;
Packit Service af52df
    char *loc_num_str = NULL;
Packit Service af52df
    bool at_radix = false;
Packit Service af52df
Packit Service af52df
    mpf_init2 (cur_val, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_set_z (cur_val, size->bytes);
Packit Service af52df
Packit Service af52df
    if (min_unit == BS_BUNIT_UNDEF)
Packit Service af52df
        min_unit = BS_BUNIT_B;
Packit Service af52df
Packit Service af52df
    sign = mpf_sgn (cur_val);
Packit Service af52df
    mpf_abs (cur_val, cur_val);
Packit Service af52df
Packit Service af52df
    mpf_div_2exp (cur_val, cur_val, 10 * (min_unit - BS_BUNIT_B));
Packit Service af52df
    while ((mpf_cmp_ui (cur_val, 1024) > 0) && min_unit != BS_BUNIT_YiB) {
Packit Service af52df
        mpf_div_2exp (cur_val, cur_val, 10);
Packit Service af52df
        min_unit++;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    if (sign == -1)
Packit Service af52df
        mpf_neg (cur_val, cur_val);
Packit Service af52df
Packit Service af52df
    len = gmp_asprintf (&num_str, "%.*Ff", max_places >= 0 ? max_places : BS_FLOAT_PREC_BITS,
Packit Service af52df
                        cur_val);
Packit Service af52df
    mpf_clear (cur_val);
Packit Service af52df
Packit Service af52df
    /* should use the proper radix char according to @xlate */
Packit Service af52df
    radix_char = nl_langinfo (RADIXCHAR);
Packit Service af52df
    if (!xlate) {
Packit Service af52df
        loc_num_str = replace_str_with_str (num_str, radix_char, ".");
Packit Service af52df
        free (num_str);
Packit Service af52df
        radix_char = ".";
Packit Service af52df
    } else
Packit Service af52df
        loc_num_str = num_str;
Packit Service af52df
Packit Service af52df
    /* remove trailing zeros and the radix char */
Packit Service af52df
    /* if max_places == 0, there can't be anything trailing */
Packit Service af52df
    if (max_places != 0) {
Packit Service af52df
        zero = loc_num_str + (len - 1);
Packit Service af52df
        while ((zero != loc_num_str) && ((*zero == '0') || (*zero == *radix_char)) && !at_radix) {
Packit Service af52df
            at_radix = *zero == *radix_char;
Packit Service af52df
            zero--;
Packit Service af52df
        }
Packit Service af52df
        zero[1] = '\0';
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    ret = strdup_printf ("%s %s", loc_num_str, xlate ? _(b_units[min_unit - BS_BUNIT_B]) : b_units[min_unit - BS_BUNIT_B]);
Packit Service af52df
    free (loc_num_str);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/***************
Packit Service af52df
 * ARITHMETIC *
Packit Service af52df
 ***************/
Packit Service af52df
/**
Packit Service af52df
 * bs_size_add:
Packit Service af52df
 *
Packit Service af52df
 * Add two sizes.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize which is a sum of @size1 and @size2
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_add (const BSSize size1, const BSSize size2) {
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    mpz_add (ret->bytes, size1->bytes, size2->bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_grow:
Packit Service af52df
 *
Packit Service af52df
 * Grows @size1 by @size2. IOW, adds @size2 to @size1 in-place (modifying
Packit Service af52df
 * @size1).
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_add().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size1 modified by adding @size2 to it
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_grow (BSSize size1, const BSSize size2) {
Packit Service af52df
    mpz_add (size1->bytes, size1->bytes, size2->bytes);
Packit Service af52df
Packit Service af52df
    return size1;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_add_bytes:
Packit Service af52df
 *
Packit Service af52df
 * Add @bytes to the @size. To add a negative number of bytes use
Packit Service af52df
 * bs_size_sub_bytes().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize which is a sum of @size and @bytes
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_add_bytes (const BSSize size, uint64_t bytes) {
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    do_64bit_add_sub (mpz_add_ui, ret->bytes, size->bytes, bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_grow_bytes:
Packit Service af52df
 *
Packit Service af52df
 * Grows @size by @bytes. IOW, adds @bytes to @size in-place (modifying @size).
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_add_bytes().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size modified by adding @bytes to it
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_grow_bytes (BSSize size, const uint64_t bytes) {
Packit Service af52df
    do_64bit_add_sub (mpz_add_ui, size->bytes, size->bytes, bytes);
Packit Service af52df
Packit Service af52df
    return size;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_sub:
Packit Service af52df
 *
Packit Service af52df
 * Subtract @size2 from @size1.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize which is equals to @size1 - @size2
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_sub (const BSSize size1, const BSSize size2) {
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    mpz_sub (ret->bytes, size1->bytes, size2->bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_shrink:
Packit Service af52df
 *
Packit Service af52df
 * Shrinks @size1 by @size2. IOW, subtracts @size2 from @size1 in-place
Packit Service af52df
 * (modifying @size1).
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_sub().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size1 modified by subtracting @size2 from it
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_shrink (BSSize size1, const BSSize size2) {
Packit Service af52df
    mpz_sub (size1->bytes, size1->bytes, size2->bytes);
Packit Service af52df
Packit Service af52df
    return size1;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_sub_bytes:
Packit Service af52df
 *
Packit Service af52df
 * Subtract @bytes from the @size. To subtract a negative number of bytes use
Packit Service af52df
 * bs_size_add_bytes().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize which equals to @size - @bytes
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_sub_bytes (const BSSize size, uint64_t bytes) {
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    do_64bit_add_sub (mpz_sub_ui, ret->bytes, size->bytes, bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_shrink_bytes:
Packit Service af52df
 *
Packit Service af52df
 * Shrinks @size by @bytes. IOW, subtracts @bytes from @size in-place
Packit Service af52df
 * (modifying @size). To shrink by a negative number of bytes use
Packit Service af52df
 * bs_size_grow_bytes().
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_sub_bytes().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size modified by subtracting @bytes from it
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_shrink_bytes (BSSize size, uint64_t bytes) {
Packit Service af52df
    do_64bit_add_sub (mpz_sub_ui, size->bytes, size->bytes, bytes);
Packit Service af52df
Packit Service af52df
    return size;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_mul_int:
Packit Service af52df
 *
Packit Service af52df
 * Multiply @size by @times.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize which equals to @size * @times
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_mul_int (const BSSize size, uint64_t times) {
Packit Service af52df
    BSSize ret = bs_size_new ();
Packit Service af52df
    mul_64bit (ret->bytes, size->bytes, times);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_grow_mul_int:
Packit Service af52df
 *
Packit Service af52df
 * Grow @size @times times. IOW, multiply @size by @times in-place.
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_mul_int().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size modified by growing it @times times
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_grow_mul_int (BSSize size, uint64_t times) {
Packit Service af52df
    mul_64bit (size->bytes, size->bytes, times);
Packit Service af52df
Packit Service af52df
    return size;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_mul_float_str:
Packit Service af52df
 *
Packit Service af52df
 * Multiply @size by the floating-point number @float_str represents.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new #BSSize instance which equals to
Packit Service af52df
 *                           @size * @times_str
Packit Service af52df
 *
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_mul_float_str (const BSSize size, const char *float_str, BSError **error) {
Packit Service af52df
    mpf_t op1, op2;
Packit Service af52df
    int status = 0;
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
    const char *radix_char = NULL;
Packit Service af52df
    char *loc_float_str = NULL;
Packit Service af52df
Packit Service af52df
    radix_char = nl_langinfo (RADIXCHAR);
Packit Service af52df
Packit Service af52df
    mpf_init2 (op1, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_init2 (op2, BS_FLOAT_PREC_BITS);
Packit Service af52df
Packit Service af52df
    mpf_set_z (op1, size->bytes);
Packit Service af52df
    loc_float_str = replace_char_with_str (float_str, '.', radix_char);
Packit Service af52df
    status = mpf_set_str (op2, loc_float_str, 10);
Packit Service af52df
    if (status != 0) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("'%s' is not a valid floating point number string", loc_float_str));
Packit Service af52df
        free (loc_float_str);
Packit Service af52df
        mpf_clears (op1, op2, NULL);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
    free (loc_float_str);
Packit Service af52df
Packit Service af52df
    mpf_mul (op1, op1, op2);
Packit Service af52df
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpz_set_f (ret->bytes, op1);
Packit Service af52df
    mpf_clears (op1, op2, NULL);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_grow_mul_float_str:
Packit Service af52df
 *
Packit Service af52df
 * Grow @size by the floating-point number @float_str represents times. IOW,
Packit Service af52df
 * multiply @size by @float_str in-place.
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_grow_mul_float_str().
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size modified by growing it @float_str times.
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_grow_mul_float_str (BSSize size, const char *float_str, BSError **error) {
Packit Service af52df
    mpf_t op1, op2;
Packit Service af52df
    int status = 0;
Packit Service af52df
    const char *radix_char = NULL;
Packit Service af52df
    char *loc_float_str = NULL;
Packit Service af52df
Packit Service af52df
    radix_char = nl_langinfo (RADIXCHAR);
Packit Service af52df
Packit Service af52df
    mpf_init2 (op1, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_init2 (op2, BS_FLOAT_PREC_BITS);
Packit Service af52df
Packit Service af52df
    mpf_set_z (op1, size->bytes);
Packit Service af52df
    loc_float_str = replace_char_with_str (float_str, '.', radix_char);
Packit Service af52df
    status = mpf_set_str (op2, loc_float_str, 10);
Packit Service af52df
    if (status != 0) {
Packit Service af52df
        set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("'%s' is not a valid floating point number string", loc_float_str));
Packit Service af52df
        free (loc_float_str);
Packit Service af52df
        mpf_clears (op1, op2, NULL);
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
    free (loc_float_str);
Packit Service af52df
Packit Service af52df
    mpf_mul (op1, op1, op2);
Packit Service af52df
Packit Service af52df
    mpz_set_f (size->bytes, op1);
Packit Service af52df
    mpf_clears (op1, op2, NULL);
Packit Service af52df
Packit Service af52df
    return size;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_div:
Packit Service af52df
 * @sgn: (allow-none) (out): sign of the result
Packit Service af52df
 *
Packit Service af52df
 * Divide @size1 by @size2. Gives the answer to the question "How many times
Packit Service af52df
 * does @size2 fit in @size1?".
Packit Service af52df
 *
Packit Service af52df
 * Returns: integer number x so that x * @size1 < @size2 and (x+1) * @size1 > @size2
Packit Service af52df
 *          (IOW, @size1 / @size2 using integer division)
Packit Service af52df
 */
Packit Service af52df
uint64_t bs_size_div (const BSSize size1, const BSSize size2, int *sgn, BSError **error) {
Packit Service af52df
    mpz_t result;
Packit Service af52df
    uint64_t ret = 0;
Packit Service af52df
Packit Service af52df
    if (mpz_cmp_ui (size2->bytes, 0) == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    if (sgn)
Packit Service af52df
        *sgn = mpz_sgn (size1->bytes) * mpz_sgn (size2->bytes);
Packit Service af52df
    mpz_init (result);
Packit Service af52df
    mpz_tdiv_q (result, size1->bytes, size2->bytes);
Packit Service af52df
Packit Service af52df
    if (mpz_cmp_ui (result, UINT64_MAX) > 0) {
Packit Service af52df
        set_error (error, BS_ERROR_OVER, strdup_printf ("The size is too big, cannot be returned as a 64bit number"));
Packit Service af52df
        mpz_clear (result);
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
    ret = (uint64_t) mpz_get_ui (result);
Packit Service af52df
Packit Service af52df
    mpz_clear (result);
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_div_int:
Packit Service af52df
 *
Packit Service af52df
 * Divide @size by @divisor. Gives the answer to the question "What is the size
Packit Service af52df
 * of each chunk if @size is split into a @divisor number of pieces?"
Packit Service af52df
 *
Packit Service af52df
 * Note: Due to the limitations of the current implementation the maximum value
Packit Service af52df
 * @divisor is ULONG_MAX (which can differ from UINT64_MAX). An error
Packit Service af52df
 * (BS_ERROR_OVER) is returned if overflow happens.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a #BSSize instance x so that x * @divisor = @size,
Packit Service af52df
 *                           rounded to a number of bytes
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_div_int (const BSSize size, uint64_t divisor, BSError **error) {
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
Packit Service af52df
    if (divisor == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return NULL;
Packit Service af52df
    } else if (divisor > ULONG_MAX) {
Packit Service af52df
        set_error (error, BS_ERROR_OVER, strdup_printf ("Divisor too big, must be less or equal to %lu", ULONG_MAX));
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpz_tdiv_q_ui (ret->bytes, size->bytes, divisor);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_shrink_div_int:
Packit Service af52df
 *
Packit Service af52df
 * Shrink @size by dividing by @divisor. IOW, divide @size by @divisor in-place.
Packit Service af52df
 *
Packit Service af52df
 * Basically an in-place variant of bs_size_div_int().
Packit Service af52df
 *
Packit Service af52df
 * Note: Due to the limitations of the current implementation the maximum value
Packit Service af52df
 * @divisor is ULONG_MAX (which can differ from UINT64_MAX). An error
Packit Service af52df
 * (BS_ERROR_OVER) is returned if overflow happens.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer none): @size modified by division by @divisor
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_shrink_div_int (BSSize size, uint64_t divisor, BSError **error) {
Packit Service af52df
    if (divisor == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return NULL;
Packit Service af52df
    } else if (divisor > ULONG_MAX) {
Packit Service af52df
        set_error (error, BS_ERROR_OVER, strdup_printf ("Divisor too big, must be less or equal to %lu", ULONG_MAX));
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpz_tdiv_q_ui (size->bytes, size->bytes, divisor);
Packit Service af52df
Packit Service af52df
    return size;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_true_div:
Packit Service af52df
 *
Packit Service af52df
 * Divides @size1 by @size2.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a string representing the floating-point number
Packit Service af52df
 *                           that equals to @size1 / @size2
Packit Service af52df
 */
Packit Service af52df
char* bs_size_true_div (const BSSize size1, const BSSize size2, BSError **error) {
Packit Service af52df
    mpf_t op1;
Packit Service af52df
    mpf_t op2;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
Packit Service af52df
    if (mpz_cmp_ui (size2->bytes, 0) == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf("Division by zero"));
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpf_init2 (op1, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_init2 (op2, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_set_z (op1, size1->bytes);
Packit Service af52df
    mpf_set_z (op2, size2->bytes);
Packit Service af52df
Packit Service af52df
    mpf_div (op1, op1, op2);
Packit Service af52df
Packit Service af52df
    gmp_asprintf (&ret, "%.*Fg", BS_FLOAT_PREC_BITS/3, op1);
Packit Service af52df
Packit Service af52df
    mpf_clears (op1, op2, NULL);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_true_div_int:
Packit Service af52df
 *
Packit Service af52df
 * Divides @size by @divisor.
Packit Service af52df
 *
Packit Service af52df
 * Note: Due to the limitations of the current implementation the maximum value
Packit Service af52df
 * @divisor is ULONG_MAX (which can differ from UINT64_MAX). An error
Packit Service af52df
 * (BS_ERROR_OVER) is returned if overflow happens.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a string representing the floating-point number
Packit Service af52df
 *                           that equals to @size / @divisor
Packit Service af52df
 */
Packit Service af52df
char* bs_size_true_div_int (const BSSize size, uint64_t divisor, BSError **error) {
Packit Service af52df
    mpf_t op1;
Packit Service af52df
    char *ret = NULL;
Packit Service af52df
Packit Service af52df
    if (divisor == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return 0;
Packit Service af52df
    } else if (divisor > ULONG_MAX) {
Packit Service af52df
        set_error (error, BS_ERROR_OVER, strdup_printf ("Divisor too big, must be less or equal to %lu", ULONG_MAX));
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpf_init2 (op1, BS_FLOAT_PREC_BITS);
Packit Service af52df
    mpf_set_z (op1, size->bytes);
Packit Service af52df
Packit Service af52df
    mpf_div_ui (op1, op1, divisor);
Packit Service af52df
Packit Service af52df
    gmp_asprintf (&ret, "%.*Fg", BS_FLOAT_PREC_BITS/3, op1);
Packit Service af52df
Packit Service af52df
    mpf_clear (op1);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_mod:
Packit Service af52df
 *
Packit Service af52df
 * Gives @size1 modulo @size2 (i.e. the remainder of integer division @size1 /
Packit Service af52df
 * @size2). Gives the answer to the question "If I split @size1 into chunks of
Packit Service af52df
 * size @size2, what will be the remainder?"
Packit Service af52df
 * **This function ignores the signs of the sizes.**
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a #BSSize instance that is a remainder of
Packit Service af52df
 *                           @size1 / @size2 using integer division
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_mod (const BSSize size1, const BSSize size2, BSError **error) {
Packit Service af52df
    mpz_t aux;
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
    if (mpz_cmp_ui (size2->bytes, 0) == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return 0;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpz_init (aux);
Packit Service af52df
    mpz_set (aux, size1->bytes);
Packit Service af52df
    if (mpz_sgn (size1->bytes) == -1)
Packit Service af52df
        /* negative @size1, get the absolute value so that we get results
Packit Service af52df
           matching the specification/documentation of this function */
Packit Service af52df
        mpz_neg (aux, aux);
Packit Service af52df
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpz_mod (ret->bytes, aux, size2->bytes);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_round_to_nearest:
Packit Service af52df
 * @round_to: to a multiple of what to round @size
Packit Service af52df
 * @dir: %BS_ROUND_DIR_UP to round up (to the nearest multiple of @round_to
Packit Service af52df
 *       bigger than @size) or %BS_ROUND_DIR_DOWN to round down (to the
Packit Service af52df
 *       nearest multiple of @round_to smaller than @size)
Packit Service af52df
 *
Packit Service af52df
 * Round @size to the nearest multiple of @round_to according to the direction
Packit Service af52df
 * given by @dir.
Packit Service af52df
 *
Packit Service af52df
 * Returns: (transfer full): a new instance of #BSSize that is @size rounded to
Packit Service af52df
 *                           a multiple of @round_to according to @dir
Packit Service af52df
 */
Packit Service af52df
BSSize bs_size_round_to_nearest (const BSSize size, const BSSize round_to, BSRoundDir dir, BSError **error) {
Packit Service af52df
    BSSize ret = NULL;
Packit Service af52df
    mpz_t q;
Packit Service af52df
    mpz_t aux_size;
Packit Service af52df
Packit Service af52df
    if (mpz_cmp_ui (round_to->bytes, 0) == 0) {
Packit Service af52df
        set_error (error, BS_ERROR_ZERO_DIV, strdup_printf ("Division by zero"));
Packit Service af52df
        return NULL;
Packit Service af52df
    }
Packit Service af52df
Packit Service af52df
    mpz_init (q);
Packit Service af52df
Packit Service af52df
    if (dir == BS_ROUND_DIR_UP) {
Packit Service af52df
        mpz_cdiv_q (q, size->bytes, round_to->bytes);
Packit Service af52df
    } else if (dir == BS_ROUND_DIR_HALF_UP) {
Packit Service af52df
        /* round half up == add half of what to round to and round down */
Packit Service af52df
        mpz_init (aux_size);
Packit Service af52df
        mpz_fdiv_q_ui (aux_size, round_to->bytes, 2);
Packit Service af52df
        mpz_add (aux_size, aux_size, size->bytes);
Packit Service af52df
        mpz_fdiv_q (q, aux_size, round_to->bytes);
Packit Service af52df
        mpz_clear (aux_size);
Packit Service af52df
    } else
Packit Service af52df
        mpz_fdiv_q (q, size->bytes, round_to->bytes);
Packit Service af52df
Packit Service af52df
    ret = bs_size_new ();
Packit Service af52df
    mpz_mul (ret->bytes, q, round_to->bytes);
Packit Service af52df
Packit Service af52df
    mpz_clear (q);
Packit Service af52df
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
Packit Service af52df
/***************
Packit Service af52df
 * COMPARISONS *
Packit Service af52df
 ***************/
Packit Service af52df
/**
Packit Service af52df
 * bs_size_cmp:
Packit Service af52df
 * @abs: whether to compare absolute values of @size1 and @size2 instead
Packit Service af52df
 *
Packit Service af52df
 * Compare @size1 and @size2. This function behaves like the standard *cmp*()
Packit Service af52df
 * functions.
Packit Service af52df
 *
Packit Service af52df
 * Returns: -1, 0, or 1 if @size1 is smaller, equal to or bigger than
Packit Service af52df
 *          @size2 respectively comparing absolute values if @abs is %true
Packit Service af52df
 */
Packit Service af52df
int bs_size_cmp (const BSSize size1, const BSSize size2, bool abs) {
Packit Service af52df
    int ret = 0;
Packit Service af52df
    if (abs)
Packit Service af52df
        ret = mpz_cmpabs (size1->bytes, size2->bytes);
Packit Service af52df
    else
Packit Service af52df
        ret = mpz_cmp (size1->bytes, size2->bytes);
Packit Service af52df
    /* make sure we don't return things like 2 or -2 (which GMP can give us) */
Packit Service af52df
    if (ret > 0)
Packit Service af52df
        ret = 1;
Packit Service af52df
    else if (ret < 0)
Packit Service af52df
        ret = -1;
Packit Service af52df
    return ret;
Packit Service af52df
}
Packit Service af52df
Packit Service af52df
/**
Packit Service af52df
 * bs_size_cmp_bytes:
Packit Service af52df
 * @abs: whether to compare absolute values of @size and @bytes instead.
Packit Service af52df
 *
Packit Service af52df
 * Compare @size and @bytes, i.e. the number of bytes @size has with
Packit Service af52df
 * @bytes. This function behaves like the standard *cmp*() functions.
Packit Service af52df
 *
Packit Service af52df
 * Returns: -1, 0, or 1 if @size is smaller, equal to or bigger than
Packit Service af52df
 *          @bytes respectively comparing absolute values if @abs is %true
Packit Service af52df
 */
Packit Service af52df
int bs_size_cmp_bytes (const BSSize size, uint64_t bytes, bool abs) {
Packit Service af52df
    int ret = 0;
Packit Service af52df
    if (abs)
Packit Service af52df
        ret = mpz_cmpabs_ui (size->bytes, bytes);
Packit Service af52df
    else
Packit Service af52df
        ret = mpz_cmp_ui (size->bytes, bytes);
Packit Service af52df
    /* make sure we don't return things like 2 or -2 (which GMP can give us) */
Packit Service af52df
    if (ret > 0)
Packit Service af52df
        ret = 1;
Packit Service af52df
    else if (ret < 0)
Packit Service af52df
        ret = -1;
Packit Service af52df
    return ret;
Packit Service af52df
}