/*
 * Copyright (c) 2008-2015 Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "Blob.h"
#include <stdexcept>
#include <stdlib.h>
#include <string.h>

Blob::Blob()
    : m_data(0)
    , m_length(0)
{
}

//! Makes a local copy of the \a data argument.
//!
Blob::Blob(const uint8_t *data, unsigned length)
    : m_data(0)
    , m_length(length)
{
    m_data = reinterpret_cast<uint8_t *>(malloc(length));
    memcpy(m_data, data, length);
}

//! Makes a local copy of the data owned by \a other.
//!
Blob::Blob(const Blob &other)
    : m_data(0)
    , m_length(other.m_length)
{
    m_data = reinterpret_cast<uint8_t *>(malloc(m_length));
    memcpy(m_data, other.m_data, m_length);
}

//! Disposes of the binary data associated with this object.
Blob::~Blob()
{
    if (m_data)
    {
        free(m_data);
    }
}

//! Copies \a data onto the blob's data. The blob does not assume ownership
//! of \a data.
//!
//! \param data Pointer to a buffer containing the data which will be copied
//!		into the blob.
//! \param length Number of bytes pointed to by \a data.
void Blob::setData(const uint8_t *data, unsigned length)
{
    setLength(length);
    memcpy(m_data, data, length);
}

//! Sets the #m_length member variable to \a length and resizes #m_data to
//! the new length. The contents of #m_data past any previous contents are undefined.
//! If the new \a length is 0 then the data will be freed and a subsequent call
//! to getData() will return NULL.
//!
//! \param length New length of the blob's data in bytes.
void Blob::setLength(unsigned length)
{
    if (length == 0)
    {
        clear();
        return;
    }

    // Allocate new block.
    if (!m_data)
    {
        m_data = reinterpret_cast<uint8_t *>(malloc(length));
        if (!m_data)
        {
            throw std::runtime_error("failed to allocate memory");
        }
    }
    // Reallocate previous block.
    else
    {
        void *newBlob = realloc(m_data, length);
        if (!newBlob)
        {
            throw std::runtime_error("failed to reallocate memory");
        }
        m_data = reinterpret_cast<uint8_t *>(newBlob);
    }

    // Set length.
    m_length = length;
}

void Blob::append(const uint8_t *newData, unsigned newDataLength)
{
    unsigned oldLength = m_length;

    setLength(m_length + newDataLength);

    memcpy(m_data + oldLength, newData, newDataLength);
}

void Blob::clear()
{
    if (m_data)
    {
        free(m_data);
        m_data = NULL;
    }

    m_length = 0;
}

void Blob::relinquish()
{
    m_data = NULL;
    m_length = 0;
}
