/*
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
  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 _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif


#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "compat.h"
#include "xlator.h"

#define ONE 1ULL
#define PRESENT_D_OFF_BITS 63
#define BACKEND_D_OFF_BITS 63
#define TOP_BIT (ONE << (PRESENT_D_OFF_BITS - 1))
#define MASK (~0ULL)
#define SHIFT_BITS (max (0, (BACKEND_D_OFF_BITS - PRESENT_D_OFF_BITS + 1)))
#define PRESENT_MASK (MASK >> (64 - PRESENT_D_OFF_BITS))

static uint64_t
bits_for (uint64_t num)
{
	uint64_t bits = 0, ctrl = 1;

	while (ctrl < num) {
		ctrl *= 2;
		bits++;
	}

	return bits;
}

int
gf_deitransform(xlator_t *this,
                uint64_t offset)
{
        int         cnt = 0;
        int         max = 0;
        int         max_bits = 0;
        uint64_t    off_mask = 0;
        uint64_t    host_mask = 0;

        max = glusterfs_get_leaf_count(this->graph);

	if (max == 1) {
		cnt = 0;
		goto out;
	}

        if (offset & TOP_BIT) {
                /* HUGE d_off */
                max_bits = bits_for (max);
                off_mask = (MASK << max_bits);
                host_mask = ~(off_mask);

                cnt = offset & host_mask;
	} else {
                /* small d_off */
                cnt = offset % max;
        }
out:
        return cnt;
}

uint64_t
gf_dirent_orig_offset(xlator_t *this,
                      uint64_t offset)
{
        int         max = 0;
        int         max_bits = 0;
        uint64_t    off_mask = 0;
        uint64_t    orig_offset;

        max = glusterfs_get_leaf_count(this->graph);

	if (max == 1) {
                orig_offset = offset;
		goto out;
	}

        if (offset & TOP_BIT) {
                /* HUGE d_off */
                max_bits = bits_for (max);
                off_mask = (MASK << max_bits);
                orig_offset = ((offset & ~TOP_BIT) & off_mask) << SHIFT_BITS;
	} else {
                /* small d_off */
                orig_offset = offset / max;
        }
out:
        return orig_offset;
}

int
gf_itransform (xlator_t *this, uint64_t x, uint64_t *y_p, int client_id)
{
        int         max = 0;
        uint64_t    y = 0;
        uint64_t    hi_mask = 0;
        uint64_t    off_mask = 0;
        int         max_bits = 0;

        if (x == ((uint64_t) -1)) {
                y = (uint64_t) -1;
                goto out;
        }

        if (!x) {
                y = 0;
                goto out;
        }

        max = glusterfs_get_leaf_count(this->graph);

	if (max == 1) {
		y = x;
		goto out;
	}

        max_bits = bits_for (max);

        hi_mask = ~(PRESENT_MASK >> (max_bits + 1));

        if (x & hi_mask) {
                /* HUGE d_off */
                off_mask = MASK << max_bits;
                y = TOP_BIT | ((x >> SHIFT_BITS) & off_mask) | client_id;
        } else {
                /* small d_off */
                y = ((x * max) + client_id);
        }

out:
        if (y_p)
                *y_p = y;

        return 0;
}

gf_dirent_t *
gf_dirent_for_name (const char *name)
{
        gf_dirent_t *gf_dirent = NULL;

        /* TODO: use mem-pool */
        gf_dirent = GF_CALLOC (gf_dirent_size (name), 1,
                               gf_common_mt_gf_dirent_t);
        if (!gf_dirent)
                return NULL;

        INIT_LIST_HEAD (&gf_dirent->list);
        strcpy (gf_dirent->d_name, name);

        gf_dirent->d_off = 0;
        gf_dirent->d_ino = -1;
        gf_dirent->d_type = 0;
        gf_dirent->d_len = strlen (name);

        return gf_dirent;
}

void
gf_dirent_entry_free (gf_dirent_t *entry)
{
        if (!entry)
                return;

        if (entry->dict)
                dict_unref (entry->dict);
        if (entry->inode)
                inode_unref (entry->inode);

        list_del (&entry->list);
        GF_FREE (entry);
}

void
gf_dirent_free (gf_dirent_t *entries)
{
        gf_dirent_t *entry = NULL;
        gf_dirent_t *tmp = NULL;

        if (!entries)
                return;

        if (list_empty (&entries->list))
                return;

        list_for_each_entry_safe (entry, tmp, &entries->list, list) {
                gf_dirent_entry_free (entry);
        }
}

gf_dirent_t *
entry_copy (gf_dirent_t *source)
{
        gf_dirent_t *sink = NULL;

        sink = gf_dirent_for_name (source->d_name);

        sink->d_off = source->d_off;
        sink->d_ino = source->d_ino;
        sink->d_type = source->d_type;
        sink->d_stat = source->d_stat;

	if (source->inode)
		sink->inode = inode_ref (source->inode);
        return sink;
}

void
gf_link_inode_from_dirent (xlator_t *this, inode_t *parent, gf_dirent_t *entry)
{
        inode_t     *link_inode = NULL;
        inode_t     *tmp        = NULL;

        if (!entry->inode)
                return;
        link_inode = inode_link (entry->inode, parent,
                                 entry->d_name, &entry->d_stat);
        if (!link_inode)
                return;

        inode_lookup (link_inode);
        tmp = entry->inode;
        entry->inode = link_inode;
        inode_unref (tmp);
}

/* TODO: Currently, with this function, we will be breaking the
   policy of 1-1 mapping of kernel nlookup refs with our inode_t's
   nlookup count.
   Need more thoughts before finalizing this function
*/
int
gf_link_inodes_from_dirent (xlator_t *this, inode_t *parent,
                            gf_dirent_t *entries)
{
        gf_dirent_t *entry      = NULL;

        list_for_each_entry (entry, &entries->list, list) {
                gf_link_inode_from_dirent (this, parent, entry);
        }

        return 0;
}
