//------------------------------------------------------------------------------
// GB_import: import a matrix in any format
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2025, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

// This method takes O(1) time and memory, unless secure is true (used
// when the input data is not trusted).

// The input arrays Ap, Ah, Ab, Ai, and Ax are assumed to be in the data arena
// defined by the current Context, or the global context if no Context is
// engaged.  Results are undefined if these arrays are in a different arena.

// The output matrix A is created in the same data arena.  If packing is true,
// the header for A remains unchanged and it stays in its same arena.
// Otherwise, the new header for A is created in the header arena defined by
// the current Context, or the global context if no Context is enganged.

#include "import_export/GB_export.h"

#define GB_FREE_ALL GB_Matrix_free (A) ;

GrB_Info GB_import      // import/pack a matrix in any format
(
    bool packing,       // pack if true, create and import false
                        // packing == true is historical.
                        // packing == false is used by GrB_Matrix_import

    GrB_Matrix *A,      // handle of matrix to create, or pack
    GrB_Type type,      // type of matrix to create
    uint64_t vlen,      // vector length
    uint64_t vdim,      // vector dimension
    bool is_sparse_vector,      // true if A is a sparse GrB_Vector

    // the 5 arrays:
    uint64_t **Ap,      // pointers, for sparse and hypersparse formats.
    uint64_t Ap_memsize,   // size of Ap in bytes

    uint64_t **Ah,      // vector indices for hypersparse matrices
    uint64_t Ah_memsize,   // size of Ah in bytes

    int8_t **Ab,        // bitmap, for bitmap format only.
    uint64_t Ab_memsize,   // size of Ab in bytes

    uint64_t **Ai,      // indices for hyper and sparse formats
    uint64_t Ai_memsize,   // size of Ai in bytes

    void **Ax,          // values
    uint64_t Ax_memsize,   // size of Ax in bytes

    // additional information for specific formats:
    uint64_t nvals,     // # of entries for bitmap format, or for a vector
                        // in CSC format.
    bool jumbled,       // if true, sparse/hypersparse may be jumbled.
    uint64_t nvec,      // # entries of Ah for hypersparse format.

    // information for all formats:
    int sparsity,       // hypersparse, sparse, bitmap, or full
    bool is_csc,        // if true then matrix is by-column, else by-row
    bool iso,           // if true then A is iso and only one entry is provided
                        // in Ax, regardless of nvals(A).
    // fast vs secure import:
    bool fast_import,   // if true: trust the data, if false: check it

    bool add_to_memtable,   // if true: add to debug memtable
    GB_Werk Werk
)
{

    //--------------------------------------------------------------------------
    // check inputs
    //--------------------------------------------------------------------------

    GB_RETURN_IF_NULL (A) ;

    int header_arena ;

    if (packing)
    { 
        // *A must be non-NULL for GxB_*_pack_* methods
        GB_RETURN_IF_NULL (*A) ;
        header_arena = GB_arena ((*A)->header_mem) ;
    }
    else
    { 
        // GxB*import and GrB*import: A is created by this method, including
        // the header.
        (*A) = NULL ;
        header_arena = GB_Context_header_arena ( ) ;
    }

    int data_arena = GB_Context_data_arena ( ) ;

    GB_RETURN_IF_NULL_OR_FAULTY (type) ;
    if (vlen > GB_NMAX || vdim > GB_NMAX || nvals > GB_NMAX || nvec > GB_NMAX
        || Ap_memsize > GB_NMAX || Ah_memsize > GB_NMAX || Ab_memsize > GB_NMAX
        || Ai_memsize > GB_NMAX || Ax_memsize > GB_NMAX)
    { 
        return (GrB_INVALID_VALUE) ;
    }

    if (Ax_memsize > 0)
    { 
        // Ax and (*Ax) are ignored if Ax_memsize is zero
        GB_RETURN_IF_NULL (Ax) ;
        GB_RETURN_IF_NULL (*Ax) ;
    }

    bool ok = true ;
    int64_t full_memsize = 0, Ax_memsize_for_non_iso ;
    if (sparsity == GxB_BITMAP || sparsity == GxB_FULL)
    { 
        ok = GB_int64_multiply ((uint64_t *) (&full_memsize), vlen, vdim) ;
        if (!ok) full_memsize = INT64_MAX ;
    }

    switch (sparsity)
    {
        case GxB_HYPERSPARSE : 
            // check Ap and get nvals
            if (nvec > vdim) return (GrB_INVALID_VALUE) ;
            if (Ap_memsize < (((vdim == 1) ? 1 : nvec)+1) * sizeof (uint64_t))
            { 
                return (GrB_INVALID_VALUE) ;
            }
            GB_RETURN_IF_NULL (Ap) ;
            GB_RETURN_IF_NULL (*Ap) ;
            nvals = (*Ap) [nvec] ;
            // check Ah
            GB_RETURN_IF_NULL (Ah) ;
            GB_RETURN_IF_NULL (*Ah) ;
            if (Ah_memsize < nvec * sizeof (uint64_t))
            { 
                return (GrB_INVALID_VALUE) ;
            }
            // check Ai
            if (Ai_memsize > 0)
            { 
                GB_RETURN_IF_NULL (Ai) ;
                GB_RETURN_IF_NULL (*Ai) ;
            }
            if (Ai_memsize < nvals * sizeof (uint64_t))
            { 
                return (GrB_INVALID_VALUE) ;
            }
            Ax_memsize_for_non_iso = nvals ;
            break ;

        case GxB_SPARSE : 
            // check Ap and get nvals
            if (!is_sparse_vector)
            {
                // GxB_Vector_import_CSC passes in Ap as a NULL, and nvals as
                // the # of entries in the vector.  All other uses of GB_import
                // pass in Ap for the sparse case
                if (Ap_memsize < (vdim+1) * sizeof (uint64_t))
                { 
                    return (GrB_INVALID_VALUE) ;
                }
                GB_RETURN_IF_NULL (Ap) ;
                GB_RETURN_IF_NULL (*Ap) ;
                nvals = (*Ap) [vdim] ;
            }
            // check Ai
            if (Ai_memsize > 0)
            { 
                GB_RETURN_IF_NULL (Ai) ;
                GB_RETURN_IF_NULL (*Ai) ;
            }
            if (Ai_memsize < nvals * sizeof (uint64_t))
            { 
                return (GrB_INVALID_VALUE) ;
            }
            Ax_memsize_for_non_iso = nvals ;
            break ;

        case GxB_BITMAP : 
            // check Ab
            if (!ok) return (GrB_INVALID_VALUE) ;
            if (Ab_memsize > 0)
            { 
                GB_RETURN_IF_NULL (Ab) ;
                GB_RETURN_IF_NULL (*Ab) ;
            }
            if (nvals > full_memsize) return (GrB_INVALID_VALUE) ;
            if (Ab_memsize < full_memsize) return (GrB_INVALID_VALUE) ;
            Ax_memsize_for_non_iso = full_memsize ;
            break ;

        case GxB_FULL : 
            Ax_memsize_for_non_iso = full_memsize ;
            break ;

        default: ;
    }

    // check the size of Ax
    if (iso)
    {
        // A is iso: Ax must be non-NULL and large enough to hold a single entry
        GBURBLE ("(iso import) ") ;
        if (*Ax == NULL || Ax_memsize < type->size)
        { 
            return (GrB_INVALID_VALUE) ;
        }
    }
    else
    {
        // A is non-iso: Ax_memsize must be zero (and Ax must then be NULL),
        // or Ax_memsize must be at least as large as Ax_memsize_for_non_iso
        if (!((Ax_memsize == 0 && *Ax == NULL) ||
              (Ax_memsize >= Ax_memsize_for_non_iso && *Ax != NULL)))
        { 
            return (GrB_INVALID_VALUE) ;
        }
    }

    //--------------------------------------------------------------------------
    // allocate/reuse the header of the matrix
    //--------------------------------------------------------------------------

    if (packing)
    { 
        // clear the content and reuse the header
        GB_phybix_free (*A) ;
    }

    // also create A->p if this is a sparse GrB_Vector
    GrB_Info info = GB_new (A, // any sparsity, new or existing user header
        type, vlen, vdim, is_sparse_vector ? GB_ph_calloc : GB_ph_null,
        is_csc, sparsity, GB_Global_hyper_switch_get ( ), nvec,
        /* OK, import as all-64-bit: */ false, false, false,
        header_arena, data_arena) ;
    if (info != GrB_SUCCESS)
    { 
        // out of memory
        return (info) ;
    }

    //--------------------------------------------------------------------------
    // import the matrix
    //--------------------------------------------------------------------------

    // transplant the user's content into the matrix
    (*A)->magic = GB_MAGIC ;
    (*A)->iso = iso ;   // OK

    switch (sparsity)
    {
        case GxB_HYPERSPARSE : 
            (*A)->nvec = nvec ;

            // import A->h, then fall through to sparse case
            (*A)->h = (*Ah) ; (*Ah) = NULL ;
            (*A)->h_mem = GB_mem (data_arena, Ah_memsize) ;
            if (add_to_memtable)
            { 
                // for debugging only
                GBMDUMP ("import A->h to memtable: %p\n", (*A)->h) ;
                GB_Global_memtable_add ((*A)->h, (*A)->h_mem) ;
            }
            // fall through to the sparse case

        case GxB_SPARSE : 
            (*A)->jumbled = jumbled ;   // import jumbled status
//          (*A)->nvec_nonempty = -1 ;  // not computed; delay until required
            GB_nvec_nonempty_set (*A, -1) ; // not computed until required

            (*A)->nvals = nvals ;

            if (is_sparse_vector)
            { 
                // GxB_Vector_import_CSC passes in Ap as NULL
                uint64_t *restrict Ap = (*A)->p ;       // OK; 64-bit only
                Ap [1] = nvals ;
            }
            else
            { 
                // import A->p, unless already created for a sparse CSC vector
                (*A)->p = (*Ap) ; (*Ap) = NULL ;        // OK; 64-bit only
                (*A)->p_mem = GB_mem (data_arena, Ap_memsize) ;
                if (add_to_memtable)
                { 
                    // for debugging only
                    GBMDUMP ("import A->p to memtable: %p\n", (*A)->p) ;
                    GB_Global_memtable_add ((*A)->p, (*A)->p_mem) ;
                }
            }

            // import A->i
            (*A)->i = (*Ai) ; (*Ai) = NULL ;    // OK; 64-bit only
            (*A)->i_mem = GB_mem (data_arena, Ai_memsize) ;
            if (add_to_memtable)
            { 
                // for debugging only
                GBMDUMP ("import A->i to memtable: %p\n", (*A)->i) ;
                GB_Global_memtable_add ((*A)->i, (*A)->i_mem) ;
            }
            break ;

        case GxB_BITMAP : 
            (*A)->nvals = nvals ;

            // import A->b
            (*A)->b = (*Ab) ; (*Ab) = NULL ;
            (*A)->b_mem = GB_mem (data_arena, Ab_memsize) ;
            if (add_to_memtable)
            { 
                // for debugging only
                GBMDUMP ("import A->b to memtable: %p\n", (*A)->b) ;
                GB_Global_memtable_add ((*A)->b, (*A)->b_mem) ;
            }
            break ;

        case GxB_FULL : 
            break ;

        default: ;
    }

    if (Ax != NULL)
    { 
        // import A->x
        (*A)->x = (*Ax) ; (*Ax) = NULL ;
        (*A)->x_mem = GB_mem (data_arena, Ax_memsize) ;
        if (add_to_memtable)
        { 
            // for debugging only
            GBMDUMP ("import A->x to memtable: %p\n", (*A)->x) ;
            GB_Global_memtable_add ((*A)->x, (*A)->x_mem) ;
        }
    }

    //--------------------------------------------------------------------------
    // fast vs secure import
    //--------------------------------------------------------------------------

    if (!fast_import)
    { 
        // Deserialization of untrusted data is a common security problem:
        // https://cwe.mitre.org/data/definitions/502.html
        //
        // If fast_import is true, GB_import trusts its input data, so it can
        // operate in O(1) time and memory.
        //
        // The import may be coming from untrusted data.  To this point in this
        // function, no kind of mangled data (malicious or inadvertant) can
        // cause a failure.  However, the content of the A->[phbix] arrays has
        // not been exhaustively checked.  This check takes time, so a fast
        // import that trusts the input as valid can skip this check.  The
        // import is fast by default, but if the import comes from possibily
        // untrusted sources (a file, say), then the user application should
        // use the descriptor setting:
        //
        //      GxB_set (desc, GxB_IMPORT, GxB_SECURE_IMPORT)
        //
        // and use the desc as input to GxB_Matrix_import_*.  The check does
        // not produce any output to stdout.  It just checks the matrix
        // exhaustively (and securly) and returns GrB_INVALID_OBJECT if
        // anything is amiss.  Once this check is passed, the data has been
        // validated and security is ensured.
        //
        // Since it has no descriptor, GrB_Matrix_import assumes that it
        // cannot trust its input.  The method takes O(nvals(A)) time anyway,
        // since it must copy the data from input arrays.
        //
        // The GxB_Matrix_import_* assumes the data can be trusted, since it
        // is designed like the move constructor in C++, taking O(1) time by
        // default.  As a result, the descriptor default is fast, not secure.
        //
        // The time for this check is proportional to the size of the 5 input
        // arrays, far higher than the O(1) time for the fast import.  However,
        // this check is essential if the input data is not trusted.
        GBURBLE ("(secure import) ") ;
        GB_OK (GB_matvec_check (*A, "secure import", GxB_SILENT, NULL, "")) ;
    }

    //--------------------------------------------------------------------------
    // import is successful
    //--------------------------------------------------------------------------

    // If debug is enabled, this check repeats the GB_matvec_check for the
    // secure import.
    ASSERT_MATRIX_OK (*A, "A imported", GB0) ;
    return (GrB_SUCCESS) ;
}

