/*
  Copyright (c) 2012-2014 DataLab, s.l. <http://www.datalab.es>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#ifndef __EC_HELPERS_H__
#define __EC_HELPERS_H__

#include "ec-types.h"

#define EC_ERR(_x) ((void *)-(intptr_t)(_x))
#define EC_IS_ERR(_x) (((uintptr_t)(_x) & ~0xfffULL) == ~0xfffULL)
#define EC_GET_ERR(_x) ((int32_t)(intptr_t)(_x))

#define EC_ALIGN_CHECK(_ptr, _align) ((((uintptr_t)(_ptr)) & ((_align)-1)) == 0)

const char *
ec_bin(char *str, size_t size, uint64_t value, int32_t digits);
const char *
ec_fop_name(int32_t id);
void
ec_trace(const char *event, ec_fop_data_t *fop, const char *fmt, ...);
int32_t
ec_bits_consume(uint64_t *n);
size_t
ec_iov_copy_to(void *dst, struct iovec *vector, int32_t count, off_t offset,
               size_t size);
int32_t
ec_buffer_alloc(xlator_t *xl, size_t size, struct iobref **piobref, void **ptr);
int32_t
ec_dict_set_array(dict_t *dict, char *key, uint64_t *value, int32_t size);
int32_t
ec_dict_get_array(dict_t *dict, char *key, uint64_t value[], int32_t size);

int32_t
ec_dict_del_array(dict_t *dict, char *key, uint64_t *value, int32_t size);
int32_t
ec_dict_set_number(dict_t *dict, char *key, uint64_t value);
int32_t
ec_dict_del_number(dict_t *dict, char *key, uint64_t *value);
int32_t
ec_dict_set_config(dict_t *dict, char *key, ec_config_t *config);
int32_t
ec_dict_del_config(dict_t *dict, char *key, ec_config_t *config);

int32_t
ec_loc_parent(xlator_t *xl, loc_t *loc, loc_t *parent);
int32_t
ec_loc_update(xlator_t *xl, loc_t *loc, inode_t *inode, struct iatt *iatt);

int32_t
ec_loc_from_fd(xlator_t *xl, loc_t *loc, fd_t *fd);
int32_t
ec_loc_from_loc(xlator_t *xl, loc_t *dst, loc_t *src);

void
ec_owner_set(call_frame_t *frame, void *owner);
void
ec_owner_copy(call_frame_t *frame, gf_lkowner_t *owner);

ec_inode_t *
__ec_inode_get(inode_t *inode, xlator_t *xl);
ec_inode_t *
ec_inode_get(inode_t *inode, xlator_t *xl);
ec_fd_t *
__ec_fd_get(fd_t *fd, xlator_t *xl);
ec_fd_t *
ec_fd_get(fd_t *fd, xlator_t *xl);

static inline uint32_t
ec_adjust_size_down(ec_t *ec, uint64_t *value, gf_boolean_t scale)
{
    uint64_t head, tmp;

    tmp = *value;
    head = tmp % ec->stripe_size;
    tmp -= head;

    if (scale) {
        tmp /= ec->fragments;
    }

    *value = tmp;

    return (uint32_t)head;
}

/* This function can cause an overflow if the passed value is too near to the
 * uint64_t limit. If this happens, it returns the tail in negative form and
 * the value is set to UINT64_MAX. */
static inline int32_t
ec_adjust_size_up(ec_t *ec, uint64_t *value, gf_boolean_t scale)
{
    uint64_t tmp;
    int32_t tail;

    tmp = *value;
    /* We first adjust the value down. This never causes overflow. */
    tail = ec_adjust_size_down(ec, &tmp, scale);

    /* If the value was already aligned, tail will be 0 and nothing else
     * needs to be done. */
    if (tail != 0) {
        /* Otherwise, we need to compute the real tail and adjust the
         * returned value to the next stripe. */
        tail = ec->stripe_size - tail;
        if (scale) {
            tmp += ec->fragment_size;
        } else {
            tmp += ec->stripe_size;
            /* If no scaling is requested there's a possibility of
             * overflow. */
            if (tmp < ec->stripe_size) {
                tmp = UINT64_MAX;
                tail = -tail;
            }
        }
    }

    *value = tmp;

    return tail;
}

/* This function is equivalent to ec_adjust_size_down() but with a potentially
 * different parameter size (off_t vs uint64_t). */
static inline uint32_t
ec_adjust_offset_down(ec_t *ec, off_t *value, gf_boolean_t scale)
{
    off_t head, tmp;

    tmp = *value;
    head = tmp % ec->stripe_size;
    tmp -= head;

    if (scale) {
        tmp /= ec->fragments;
    }

    *value = tmp;

    return (uint32_t)head;
}

/* This function is equivalent to ec_adjust_size_up() but with a potentially
 * different parameter size (off_t vs uint64_t). */
static inline int32_t
ec_adjust_offset_up(ec_t *ec, off_t *value, gf_boolean_t scale)
{
    uint64_t tail, tmp;

    /* An offset is a signed type that can only have positive values, so
     * we take advantage of this to avoid overflows. We simply convert it
     * to an unsigned integer and operate normally. This won't cause an
     * overflow. Overflow is only checked when converting back to an
     * off_t. */
    tmp = *value;
    tail = ec->stripe_size;
    tail -= (tmp + tail - 1) % tail + 1;
    tmp += tail;
    if (scale) {
        /* If we are scaling, we'll never get an overflow. */
        tmp /= ec->fragments;
    } else {
        /* Check if there has been an overflow. */
        if ((off_t)tmp < 0) {
            tmp = GF_OFF_MAX;
            tail = -tail;
        }
    }

    *value = (off_t)tmp;

    return (int32_t)tail;
}

static inline int32_t
ec_is_power_of_2(uint32_t value)
{
    return (value != 0) && ((value & (value - 1)) == 0);
}

gf_boolean_t
ec_is_internal_xattr(dict_t *dict, char *key, data_t *value, void *data);

void
ec_filter_internal_xattrs(dict_t *xattr);

gf_boolean_t
ec_is_data_fop(glusterfs_fop_t fop);

int32_t
ec_launch_replace_heal(ec_t *ec);

#endif /* __EC_HELPERS_H__ */
