/* BEGIN_HEADER */
#include "mbedtls/memory_buffer_alloc.h"
#define TEST_SUITE_MEMORY_BUFFER_ALLOC

/* END_HEADER */

/* BEGIN_DEPENDENCIES
 * depends_on:MBEDTLS_MEMORY_BUFFER_ALLOC_C
 * END_DEPENDENCIES
 */

/* BEGIN_SUITE_HELPERS */
static int check_pointer( void *p )
{
    if( p == NULL )
        return( -1 );

    if( (size_t) p % MBEDTLS_MEMORY_ALIGN_MULTIPLE != 0 )
        return( -1 );

    return( 0 );
}
/* END_SUITE_HELPERS */

/* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */
void mbedtls_memory_buffer_alloc_self_test( )
{
    TEST_ASSERT( mbedtls_memory_buffer_alloc_self_test( 1 ) == 0 );
}
/* END_CASE */

/* BEGIN_CASE */
void memory_buffer_alloc_free_alloc( int a_bytes, int b_bytes, int c_bytes,
                                        int d_bytes,
                                     int free_a, int free_b, int free_c,
                                        int free_d,
                                     int e_bytes, int f_bytes )
{
    unsigned char buf[1024];
    unsigned char *ptr_a = NULL, *ptr_b = NULL, *ptr_c = NULL, *ptr_d = NULL,
                    *ptr_e = NULL, *ptr_f = NULL;

#if defined(MBEDTLS_MEMORY_DEBUG)
    size_t reported_blocks;
    size_t reported_bytes;
#endif
    size_t allocated_bytes = 0;

    mbedtls_memory_buffer_alloc_init( buf, sizeof( buf ) );

    mbedtls_memory_buffer_set_verify( MBEDTLS_MEMORY_VERIFY_ALWAYS );

    if( a_bytes > 0 )
    {
        ptr_a = mbedtls_calloc( a_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_a ) == 0 );

        allocated_bytes += a_bytes * sizeof(char);
    }

    if( b_bytes > 0 )
    {
        ptr_b = mbedtls_calloc( b_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_b ) == 0 );

        allocated_bytes += b_bytes * sizeof(char);
    }

    if( c_bytes > 0 )
    {
        ptr_c = mbedtls_calloc( c_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_c ) == 0 );

        allocated_bytes += c_bytes * sizeof(char);
    }

    if( d_bytes > 0 )
    {
        ptr_d = mbedtls_calloc( d_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_d ) == 0 );

        allocated_bytes += d_bytes * sizeof(char);
    }

#if defined(MBEDTLS_MEMORY_DEBUG)
    mbedtls_memory_buffer_alloc_cur_get( &reported_bytes, &reported_blocks );
    TEST_ASSERT( reported_bytes == allocated_bytes );
#endif

    if( free_a )
    {
        mbedtls_free( ptr_a );
        ptr_a = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

        allocated_bytes -= a_bytes * sizeof(char);
    }

    if( free_b )
    {
        mbedtls_free( ptr_b );
        ptr_b = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

        allocated_bytes -= b_bytes * sizeof(char);
    }

    if( free_c )
    {
        mbedtls_free( ptr_c );
        ptr_c = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

        allocated_bytes -= c_bytes * sizeof(char);
    }

    if( free_d )
    {
        mbedtls_free( ptr_d );
        ptr_d = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

        allocated_bytes -= d_bytes * sizeof(char);
    }

#if defined(MBEDTLS_MEMORY_DEBUG)
    mbedtls_memory_buffer_alloc_cur_get( &reported_bytes, &reported_blocks );
    TEST_ASSERT( reported_bytes == allocated_bytes );
#endif

    if( e_bytes > 0 )
    {
        ptr_e = mbedtls_calloc( e_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_e ) == 0 );
    }

    if( f_bytes > 0 )
    {
        ptr_f = mbedtls_calloc( f_bytes, sizeof(char) );
        TEST_ASSERT( check_pointer( ptr_f ) == 0 );
    }

    /* Once blocks are reallocated, the block allocated to the memory request
     * may be bigger than the request itself, which is indicated by the reported
     * bytes, and makes it hard to know what the reported size will be, so
     * we don't check the size after blocks have been reallocated. */

    if( ptr_a != NULL )
    {
        mbedtls_free( ptr_a );
        ptr_a = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

    if( ptr_b != NULL )
    {
        mbedtls_free( ptr_b );
        ptr_b = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

    if( ptr_c != NULL )
    {
        mbedtls_free( ptr_c );
        ptr_c = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

    if( ptr_d != NULL )
    {
        mbedtls_free( ptr_d );
        ptr_d = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

    if( ptr_e != NULL )
    {
        mbedtls_free( ptr_e );
        ptr_e = NULL;
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

    if( ptr_f != NULL )
    {
        mbedtls_free( ptr_f );
        ptr_f = NULL;
    }

#if defined(MBEDTLS_MEMORY_DEBUG)
    mbedtls_memory_buffer_alloc_cur_get( &reported_bytes, &reported_blocks );
    TEST_ASSERT( reported_bytes == 0 );
#endif

    TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

exit:
    mbedtls_memory_buffer_alloc_free( );
}
/* END_CASE */

/* BEGIN_CASE */
void memory_buffer_alloc_oom_test(  )
{
    unsigned char buf[1024];
    unsigned char *ptr_a = NULL, *ptr_b = NULL, *ptr_c = NULL;
#if defined(MBEDTLS_MEMORY_DEBUG)
    size_t reported_blocks, reported_bytes;
#endif

    (void)ptr_c;

    mbedtls_memory_buffer_alloc_init( buf, sizeof( buf ) );

    mbedtls_memory_buffer_set_verify( MBEDTLS_MEMORY_VERIFY_ALWAYS );

    ptr_a = mbedtls_calloc( 432, sizeof(char) );
    TEST_ASSERT( check_pointer( ptr_a ) == 0 );

    ptr_b = mbedtls_calloc( 432, sizeof(char) );
    TEST_ASSERT( check_pointer( ptr_b ) == 0 );

    ptr_c = mbedtls_calloc( 431, sizeof(char) );
    TEST_ASSERT( ptr_c == NULL );

#if defined(MBEDTLS_MEMORY_DEBUG)
    mbedtls_memory_buffer_alloc_cur_get( &reported_bytes, &reported_blocks );
    TEST_ASSERT( reported_bytes >= 864 && reported_bytes <= sizeof(buf) );
#endif

    mbedtls_free( ptr_a );
    ptr_a = NULL;
    TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

    mbedtls_free( ptr_b );
    ptr_b = NULL;
    TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

#if defined(MBEDTLS_MEMORY_DEBUG)
    mbedtls_memory_buffer_alloc_cur_get( &reported_bytes, &reported_blocks );
    TEST_ASSERT( reported_bytes == 0 );
#endif

    TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );

exit:
    mbedtls_memory_buffer_alloc_free( );
}
/* END_CASE */

/* BEGIN_CASE */
void memory_buffer_heap_too_small( )
{
    unsigned char buf[1];

    mbedtls_memory_buffer_alloc_init( buf, sizeof( buf ) );
    /* With MBEDTLS_MEMORY_DEBUG enabled, this prints a message
     * "FATAL: verification of first header failed".
     */
    TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() != 0 );
}
/* END_CASE */

/* BEGIN_CASE */
void memory_buffer_underalloc( )
{
    unsigned char buf[100];
    size_t i;

    mbedtls_memory_buffer_alloc_init( buf, sizeof( buf ) );
    for( i = 1; i < MBEDTLS_MEMORY_ALIGN_MULTIPLE; i++ )
    {
        TEST_ASSERT( mbedtls_calloc( 1,
                     (size_t)-( MBEDTLS_MEMORY_ALIGN_MULTIPLE - i ) ) == NULL );
        TEST_ASSERT( mbedtls_memory_buffer_alloc_verify() == 0 );
    }

exit:
    mbedtls_memory_buffer_alloc_free();
}
/* END_CASE */
