/*
 * This file is part of the Score-P software (http://www.score-p.org)
 *
 * Copyright (c) 2009-2012,
 * RWTH Aachen University, Germany
 *
 * Copyright (c) 2009-2012,
 * Gesellschaft fuer numerische Simulation mbH Braunschweig, Germany
 *
 * Copyright (c) 2009-2012, 2014,
 * Technische Universitaet Dresden, Germany
 *
 * Copyright (c) 2009-2012,
 * University of Oregon, Eugene, USA
 *
 * Copyright (c) 2009-2012, 2016,
 * Forschungszentrum Juelich GmbH, Germany
 *
 * Copyright (c) 2009-2012,
 * German Research School for Simulation Sciences GmbH, Juelich/Aachen, Germany
 *
 * Copyright (c) 2009-2012,
 * Technische Universitaet Muenchen, Germany
 *
 * This software may be modified and distributed under the terms of
 * a BSD-style license.  See the COPYING file in the package base
 * directory for details.
 *
 */


#ifndef OTF2_INTERNAL_ID_MAP_H
#define OTF2_INTERNAL_ID_MAP_H


/**
 *  @internal
 *
 *  @file
 *
 *  @brief      This file declares internal helper routines for the OTF2_IdMap
 *              data structure.
 */


/* Actual ID mapping data structure */
struct OTF2_IdMap_struct
{
    OTF2_IdMapMode mode;         /* Mapping mode */
    uint64_t*      items;        /* Field elements */
    uint64_t       capacity;     /* Total field size */
    uint64_t       size;         /* Number of entries used */
};


size_t
otf2_id_map_get_size( const OTF2_IdMap* idMap );


void
otf2_id_map_write( OTF2_Buffer*      bufferHandle,
                   const OTF2_IdMap* idMap );


OTF2_ErrorCode
otf2_id_map_read( OTF2_Buffer*       bufferHandle,
                  const OTF2_IdMap** idMap );


OTF2_ErrorCode
otf2_id_map_append_unsorted_id_pair_sparse( OTF2_IdMap* idMap,
                                            uint64_t    localId,
                                            uint64_t    globalId );


void
otf2_id_map_sort_sparse( OTF2_IdMap* idMap );

/*
 * Performs a binary search in @a instance (which must be a sparse mapping
 * table) for the given @a local_id. Returns 1 if the ID could be found and
 * stores the corresponding table index at the memory location pointed to by
 * @a index. Otherwise, it returns 0 and the insertion location is stored at
 * the location pointed to by @a index.
 */
static inline bool
otf2_id_map_find( const OTF2_IdMap* instance,
                  uint64_t          localId,
                  uint64_t*         index )
{
    UTILS_ASSERT( instance && index );

    long left  = 0;
    long right = ( ( long )instance->size / 2 ) - 1;

    while ( left <= right )
    {
        long mid = ( left + right ) & ~1; /* mid = [(left + right) / 2] * 2 */

        if ( instance->items[ mid ] < localId )
        {
            left = ( mid / 2 ) + 1;
        }
        else if ( instance->items[ mid ] > localId )
        {
            right = ( mid / 2 ) - 1;
        }
        else
        {
            *index = ( uint64_t )mid;
            return 1;
        }
    }

    *index = ( uint64_t )( left * 2 );
    return 0;
}


static inline OTF2_ErrorCode
otf2_id_map_get_global_id( const OTF2_IdMap* instance,
                           uint64_t          localId,
                           uint64_t*         globalId )
{
    if ( !instance || !globalId )
    {
        return UTILS_ERROR( OTF2_ERROR_INVALID_ARGUMENT,
                            "NULL pointer arguments." );
    }

    /* Use the identity function, if the localId is not in the map. */
    *globalId = localId;
    if ( instance->mode == OTF2_ID_MAP_DENSE )
    {
        if ( localId < instance->size )
        {
            *globalId = instance->items[ localId ];
        }
    }
    else
    {
        uint64_t index;
        if ( otf2_id_map_find( instance, localId, &index ) )
        {
            *globalId = instance->items[ index + 1 ];
        }
    }

    return OTF2_SUCCESS;
}


static inline OTF2_ErrorCode
otf2_id_map_get_global_id_safe( const OTF2_IdMap* instance,
                                uint64_t          localId,
                                uint64_t*         globalId )
{
    if ( !instance || !globalId )
    {
        return UTILS_ERROR( OTF2_ERROR_INVALID_ARGUMENT,
                            "NULL pointer arguments." );
    }

    if ( instance->mode == OTF2_ID_MAP_DENSE )
    {
        if ( localId < instance->size )
        {
            *globalId = instance->items[ localId ];
            return OTF2_SUCCESS;
        }
    }
    else
    {
        uint64_t index;
        if ( otf2_id_map_find( instance, localId, &index ) )
        {
            *globalId = instance->items[ index + 1 ];
            return OTF2_SUCCESS;
        }
    }

    return OTF2_ERROR_INDEX_OUT_OF_BOUNDS;
}

#endif /* OTF2_INTERNAL_ID_MAP_H */
