/**
 * @file
 * Raw memory related functionality
 * 
 * @copyright
 * @verbatim
   Copyright @ 2017 Audi Electronics Venture GmbH. All rights reserved.

       This Source Code Form is subject to the terms of the Mozilla
       Public License, v. 2.0. If a copy of the MPL was not distributed
       with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

   If it is not possible or desirable to put the notice in a particular file, then
   You may include the notice in a location (such as a LICENSE file in a
   relevant directory) where a recipient would be likely to look for such a notice.

   You may add additional accurate notices of copyright ownership.
   @endverbatim
 */
#ifndef A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED
#define A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED

#include <algorithm>

#include "a_util/memory.h"
#include "a_util/result.h"

namespace a_util
{
namespace memory
{

//define all needed error types and values locally
_MAKE_RESULT(-4, ERR_POINTER);
_MAKE_RESULT(-5, ERR_INVALID_ARG);

/// Enum describing the endianess
typedef enum
{
    bit_little_endian = 1,
    bit_big_endian = 2,
} Endianess;

/**
* Returns the endianess of the platform
* @return See \ref Endianess
*/
Endianess get_platform_endianess();

namespace detail
{
/**
* Format the bit pattern of a uint64_t value to a string
* Used for debug purposes.
*
* @param [in] value The value to print.
* @retval The bitstring
*/
std::string formatBits(uint64_t value);

/**
* Convert the endianess of a signal by correctly swapping the byte order if required.
* The variable signal is a uint64_t, but the value that needs conversion might be smaller.
* The parameter bit_length determines if a 16, 32 or 64 value should be swapped.
* For a LE system, reading BE signals of 3, 5, 6 or 7 bytes length the value will be aligned
* within a 4 or 8 byte value before swapping bytes.
*
* @param [in,out] signal      Pointer to the variable to store the read value in.
* @param [in]     endianess   Parameter describing the endianess of the bitfield to convert.
* @param [in]     bit_length   Number of bits to read.
*
* @return Returns a standard result code.
*/
a_util::result::Result convertSignalEndianess(uint64_t *signal, Endianess endianess, size_t bit_length);

/**
* Converter Base
* Contains the base methods used by all inheriting Converter classes.
*/
template<typename T>
class ConverterBase {
protected:
    /**
    * Read value from bitfield.
    * Operating on a uint64_t copy to allow bit shifting and masking operations.
    *
    * @param [in]  buffer      Pointer to the memory buffer to read from.
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] value       Pointer to the variable to store the read value in.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to read from.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result readSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T* value,
        Endianess endianess = get_platform_endianess())
    {
        /*
        *   offset_end         offset_start
        *        _                   ____
        *       | |                 |    |
        *  ....|...abcde|fghijklm|no......|.... Buffer (index 0 on the right end side)
        *          |_______________|
        *              bit_length  ^
        *                          |
        *                          start_bit
        */

        // 1) COPY relevant bytes of Buffer content to result variable.
        uint64_t result = 0; // Result value
        uint64_t ninth_byte = 0; // variable to eventually store a ninth byte from buffer
        size_t bytes_to_read = 0;
        copyBytesFromBuffer(buffer, &result, start_bit, bit_length, &ninth_byte, &bytes_to_read);

        // 2) TRIM unrelevant bits and SHIFT to align value.

        // Number of bits the start position is offset from 0 (0 for aligned signal)
        size_t offset_start = start_bit % 8;
        // Number of bits the end position is offset from the end of the last byte (0 for complete bytes)
        size_t offset_end = (8 - ((start_bit + bit_length) % 8)) % 8;

        /**********************************************************************************************
        *      Distinguish between LE and BE operating systems to get the shift operations right     *
        **********************************************************************************************/
        // On LE System
        if (get_platform_endianess() == bit_little_endian)
        {
            /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
            *
            *   ...|...abcde|fghijklm|no......|  =>  000|000abcde|fghijklm|no......|
            *
            */
            cutLeadingBits(&result, bit_length + offset_start);

            /* Shift right to align start at position 0 (also trims the right end).
            *
            *   000|000abcde|fghijklm|no......|  =>  000|00000000|0abcdefg|hijklmno|
            *
            */
            result >>= offset_start;

            // Eventually get bits from the copied 9th byte.
            if (ninth_byte > 0) // nothing to take care of if nothing was copied or all copied bits are 0.
            {
                // deleteAll unwanted bits from ninth byte.
                cutLeadingBits(&ninth_byte, (8 - offset_end));
                size_t bit_size = sizeof(result) * 8;
                // Shift requested bits from the 9th byte into the right position to be combined with result.
                ninth_byte <<= (bit_size - offset_start);
                // merge value together from all nine bytes.
                result = result | ninth_byte;
            }

            // BE Signal needs byte order swapping.
            if (endianess == bit_big_endian)
            {
                // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
                if (bit_length % 8 != 0)
                {
                    /* Shift left to align end position.
                    *
                    *   000|0abcdefg|hijklmno|  =>  000|abcdefgh|ijklmno0|
                    *
                    */
                    result <<= (offset_end + offset_start) % 8;

                    /* Shift bits within MSByte, filling the gap with 0s.
                    *
                    *   000|abcdefgh|ijklmno0|  =>  000|abcdefgh|0ijklmno|
                    *                ^                        ^
                    *              MSByte                   MSByte
                    */
                    uint8_t *ms_byte = (uint8_t*)&result;
                    ms_byte[0] >>= (offset_end + offset_start) % 8;
                }

                /* swap bytes to LE.
                *
                *   000|abcdefgh|0ijklmno|  =>  000|0ijklmno|abcdefgh|
                *
                */
                detail::convertSignalEndianess(&result, endianess, bit_length);
            }
        }
        // On BE System
        else
        {
            /* swap bytes to simulate LE shifting operations.
            *
            *   |...abcde|fghijklm|no......|...  =>  ...|no......|fghijklm|...abcde|
            *
            */
            detail::convertSignalEndianess(&result, bit_little_endian, sizeof(result) * 8);

            // LE Signal
            if (endianess == bit_little_endian)
            {
                /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
                *
                *   ...|no......|fghijklm|...abcde|  =>  ...|no......|fghijklm|000abcde|
                *
                */
                cutLeadingBits(&result, (sizeof(result) * 8) - offset_end); // Cut away only offset_end bits.

                /* Shift right to align bits within LSByte (BE Shifting!).
                *
                *   ...|no......|fghijklm|000abcde|  =>  ...|hijklmno|0abcdefg|00000000|
                *
                */
                result >>= offset_start;

                /* Shift right to align LSByte on the left side (BE Shifting!).
                *
                *   ...|hijklmno|0abcdefg|00000000|  =>  |hijklmno|0abcdefg|000
                *
                * Shift over all
                * empty bytes = (all bits - occupied bits (bit_length) - already shifted bits) / number of bytes
                */
                result >>= (((sizeof(result) * 8) - bit_length - offset_start) / sizeof(result)) * 8;

                // No further byte swap, because there has been one swap before the shift operations already.
            }
            // BE Signal
            else
            {
                /* Shift left to align bits within LSByte (now rightmost byte because of byte swap).
                * Also deletes bits from end offset.
                *
                *   ...|no......|fghijklm|...abcde|  =>  ...|........|ijklmno.|abcdefgh|
                *
                */
                result <<= offset_end;

                // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
                if (bit_length % 8 != 0)
                {
                    /* Shift bits within MSByte to move 0s to the highest bits (also deleting all unwanted bits from start offset).
                    *
                    *   ...|........|ijklmno.|abcdefgh|  =>  ...|........|0ijklmno|abcdefgh|
                    *
                    */
                    uint8_t *ms_byte = (uint8_t*)&result + (bit_length / 8); // position of the value's MSByte
                    ms_byte[0] >>= (offset_end + offset_start) % 8; // amount of unused bits within MSByte
                }

                /* swap bytes back to BE
                *
                *   ...|........|0ijklmno|abcdefgh|  =>  |abcdefgh|0ijklmno|...
                *
                */
                detail::convertSignalEndianess(&result, bit_little_endian, sizeof(result) * 8); // Change the simulated LE value back

                /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
                *
                *   |abcdefgh|0ijklmno|...  =>  |abcdefgh|0ijklmno|000
                *
                * Remove everything behind the value length plus the gap within MSByte.
                */
                cutLeadingBits(&result, bit_length + (offset_end + offset_start) % 8);
            }
        }

        // Copy the resulting value to the target variable. No Casting! Data might be lost otherwise.
        size_t sz = std::min(sizeof(*value), sizeof(result));
        a_util::memory::copy(value, sz, &result, sz);

        return a_util::result::SUCCESS;
    }

    /**
    * Write value to bitfield.
    * Operating on a uint64_t copy to allow bit shifting and masking operations.
    *
    * @param [in]  buffer      Pointer to the memory buffer to write to.
    * @param [in]  start_bit    Bit position to start writing to. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to write.
    * @param [out] value       Value to write to the bitfield.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to write to.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result writeSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T value,
        Endianess endianess = get_platform_endianess())
    {
        // 1) Copy relevant bytes of Buffer content to be overwritten.
        uint64_t buffer_copy = 0;
        uint64_t ninth_byte = 0; // storage variable for the ninth bit from buffer
        size_t bytes_to_read = 0;
        copyBytesFromBuffer(buffer, &buffer_copy, start_bit, bit_length, &ninth_byte, &bytes_to_read);

        // 2) Erase Bits from Buffer copy that will be overwritten TODO: BE LE system difference important here?
        // Number of bits the start position is offset from 0
        size_t offset_start = start_bit % 8;
        // Number of bits the end position is offset from the end of the last byte
        size_t offset_end = (8 - ((start_bit + bit_length) % 8)) % 8;
        uint64_t mask_left = ~0ULL;
        if ((bit_length + offset_start) >= (sizeof(mask_left) * 8))
        {
            mask_left = 0;
        }
        else
        {
            mask_left = ~0ULL;
            mask_left <<= (bit_length + offset_start);
        }
        uint64_t mask = ~0ULL;
        mask <<= offset_start;
        mask = ~mask;
        mask |= mask_left;

        buffer_copy &= mask;

        // 3) Copy value to UInt64 variable to work with.
        uint64_t signal;
        a_util::memory::copy(&signal, sizeof(signal), &value, sizeof(signal));

        // 4) Keep only nLength bits: Remove bits that should not be written to the Buffer.
        cutLeadingBits(&signal, bit_length);

        // 5) Shift to align at start bit position. 
        int shift_amount = (int)offset_start; // Initialized to fit LE Signal shift

        // BE Signal
        if (endianess == bit_big_endian)
        {
            // swap bytes
            detail::convertSignalEndianess(&signal, endianess, bit_length);
            // Remove gap for partial bytes within MSByte
            uint8_t *ms_byte = (uint8_t*)&signal;
            int ms_shift = (8 - (bit_length % 8)) % 8;
            ms_byte[0] <<= ms_shift;
            shift_amount -= ms_shift;
        }

        // Copy most significant byte to ninth buffer byte before losing bits with the shift.
        if ((offset_start + bit_length) > (sizeof(signal) * 8))
        {
            uint64_t signal_for_ninth_byte = signal;
            signal_for_ninth_byte >>= (sizeof(signal) - 1) * 8;
            signal_for_ninth_byte >>= (8 - offset_start); // Only LE Signal
            uint64_t mask = ~0ULL;
            mask <<= (8 - offset_end);
            ninth_byte &= mask;
            ninth_byte |= signal_for_ninth_byte;
        }

        if (shift_amount < 0)
        {
            signal >>= std::abs(shift_amount);
        }
        else
        {
            signal <<= shift_amount;
        }

        // 7) Write bytes with integrated signal back to the buffer. 
        buffer_copy |= signal;

        size_t sz = std::min(bytes_to_read, sizeof(signal));
        a_util::memory::copy(buffer + (start_bit / 8), sz, &buffer_copy, sz);

        // Eventually copy ninth byte back to buffer
        if (bytes_to_read > sizeof(signal))
        {
            a_util::memory::copy(buffer + (start_bit / 8) + sizeof(signal), 1, &ninth_byte, 1);
        }

        return a_util::result::SUCCESS;
    }

    /**
    * Set the highest bits of a uint64_t value to zero. The number of bit_length lowest bits
    * remain unchanged.
    *
    * @param [out] value       Pointer to the variable to trim.
    * @param [in]  bit_length   Number of trailing bits to remain unchanged.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result cutLeadingBits(uint64_t *value, size_t bit_length)
    {
        size_t bit_size = (sizeof(*value) * 8);
        if (bit_length < bit_size)
        {
            uint64_t mask = ~0ULL;
            mask >>= (bit_size - bit_length);
            *value &= mask;
        }

        return a_util::result::SUCCESS;
    }

    /**
    * Copy bytes_to_read number of bytes from the buffer to value and ninth_byte.
    * Determines how many bytes need to be copied to receive a copy of all bits in the range described
    * by start_bit and bit_length. The maximum for bit_length is 64, but for unaligned values the range
    * may exceed 8 bytes. In this case, the required ninth byte will be copied to ninth_byte.
    *
    * @param [in]  buffer      Pointer to the memory buffer to copy from.
    * @param [out] value       Pointer to the variable to store the copied value in.
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] ninth_byte   Pointer to the variable to eventually store a copied ninth byte in.
    * @param [out] bytes_to_read Number of bytes that need to be copied to attain all requested bits.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result copyBytesFromBuffer(uint8_t *buffer, uint64_t *value, size_t start_bit, size_t bit_length, uint64_t *ninth_byte, size_t *bytes_to_read)
    {
        // Byte within the buffer to start reading at
        size_t start_byte = start_bit / 8;

        // Number of bits to read: signal length + bits to fill in the offset on both sides for unaligned signals
        size_t bits_to_read = bit_length + (start_bit % 8);
        if ((bits_to_read % 8) > 0)
        {
            bits_to_read += (8 - (bits_to_read % 8));
        }
        // Number of bytes to read from the buffer
        *bytes_to_read = bits_to_read / 8;

        // Copy up to 8 bytes to result
        if (*bytes_to_read > (size_t)sizeof(*value))
        {
            a_util::memory::copy(value, sizeof(*value), buffer + start_byte, sizeof(*value));
        }
        else
        {
            a_util::memory::copy(value, *bytes_to_read, buffer + start_byte, *bytes_to_read);
        }

        // The max signal size is 8 byte, but if the signal is not aligned, it might spread over 9 bytes.

        if (*bytes_to_read > sizeof(*value))
        {
            // Get a copy of the most significant byte, which could not yet be saved to result
            a_util::memory::copy(ninth_byte, 1, buffer + start_byte + sizeof(*value), 1);
        }

        return a_util::result::SUCCESS;
    }
};

/// Template converter class to differentiate between float, signed and unsigned integer values.
template<typename T, int is_signed, int is_floating_point> class Converter;

/// Partially specialized class for Unsigned Integers
template<typename T>
class Converter<T, 0, 0> : public ConverterBase<T>
{
public:
    /**
    * Read unsigned integer from bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to read from.
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] value       Pointer to the variable to store the read value in.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to read from.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T* value,
        Endianess endianess)
    {
        return ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
    }

    /**
    * Write unsigned integer to bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to write to.
    * @param [in]  start_bit    Bit position to start writing to. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to write.
    * @param [out] value       Value to write to the bitfield.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to write to.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value,
        Endianess endianess)
    {
        return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
    }
};

/// Partially specialized class for Signed Integers
template<typename T>
class Converter<T, 1, 0> : public ConverterBase<T>
{
public:
    /**
    * Read signed integer from bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to read from.
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] value       Pointer to the variable to store the read value in.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to read from.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T* value,
        Endianess endianess)
    {
        a_util::result::Result res = ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
        if (res != a_util::result::SUCCESS)
        {
            return res;
        }

        // replicate sign bit
        *value <<= (sizeof(T) * 8) - bit_length;
        *value >>= (sizeof(T) * 8) - bit_length;
        return a_util::result::SUCCESS;
    }

    /**
    * Write signed integer to bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to write to.
    * @param [in]  start_bit    Bit position to start writing to. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to write.
    * @param [out] value       Value to write to the bitfield.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to write to.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value,
        Endianess endianess)
    {
        // Nothing special to take care of for writing signed integers, compared to writing unsigned integers.
        return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
    }
};

/// Specialized class for Floats (Floats are always signed!)
template<typename T>
class Converter<T, 1, 1> : public ConverterBase<T>
{
public:
    /**
    * Read tFloat from bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to read from.
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] value       Pointer to the variable to store the read value in.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to read from.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T* value,
        Endianess endianess)
    {
        // Read only values of size tFloat
        if (sizeof(T) * 8 == bit_length)
        {
            return ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
        }
        else
        {
            return ERR_INVALID_ARG;
        }
    }

    /**
    * Write tFloat to bitfield.
    *
    * @param [in]  buffer      Pointer to the memory buffer to write to.
    * @param [in]  start_bit    Bit position to start writing to. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to write.
    * @param [out] value       Value to write to the bitfield.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to write to.
    *
    * @return Returns a standard result code.
    */
    static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value,
        Endianess endianess)
    {
        // Write only values of size tFloat
        if (sizeof(T) * 8 == bit_length)
        {
            return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
        }
        else
        {
            return ERR_INVALID_ARG;
        }
    }
};

} // namespace detail

/// Bit Serializer Class
class BitSerializer
{
public:
    /**
    * Constructor
    */
    BitSerializer(void* data, size_t data_size) :
        _buffer(static_cast<uint8_t*>(data)), _buffer_bytes(data_size),
        _buffer_bits(_buffer_bytes * 8)
    {
    }

    /**
    * Default Constructor
    */
    BitSerializer() : _buffer(NULL), _buffer_bytes(0), _buffer_bits(0)
    {
    }

    /**
    * Read value from bitfield. Value can be of type tFloat or an unsigned or signed integer.
    *
    *  ....|...*****|********|**......|.... Buffer (index 0 on the right end side)
    *          |_______________|
    *              bit_length  ^
    *                          |
    *                          start_bit
    *
    * @param [in]  start_bit    Bit position to start reading from. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to read.
    * @param [out] value       Pointer to the variable to store the read value in.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to read from.
    *
    * @return Returns a standard result code.
    */
    template<typename T>
    a_util::result::Result read(size_t start_bit, size_t bit_length, T* value,
        Endianess endianess = get_platform_endianess())
    {
        // Check if in range
        a_util::result::Result result_code = checkForInvalidArguments(start_bit, bit_length, sizeof(T));
        if (result_code != a_util::result::SUCCESS)
        {
            return result_code;
        }

        // Call template function
        detail::Converter<T, std::is_signed<T>::value,
            std::is_floating_point<T>::value>
            ::read(_buffer, start_bit, bit_length, value, endianess);

        return a_util::result::SUCCESS;
    }

    /**
    * Write value to bitfield. Value can be of type tFloat or an unsigned or signed integer.
    *
    *  ....|...*****|********|**......|.... Buffer (index 0 on the right end side)
    *          |_______________|
    *              bit_length  ^
    *                          |
    *                          start_bit
    *
    * @param [in]  start_bit    Bit position to start writing to. The least significant bit
    *                           has the index 0.
    * @param [in]  bit_length   Number of bits to write.
    * @param [out] value       Value to write to the bitfield.
    * @param [in]  endianess   Parameter describing the endianess of the bitfield to write to.
    *
    * @return Returns a standard result code.
    */
    template<typename T>
    a_util::result::Result write(size_t start_bit, size_t bit_length, T value,
        Endianess endianess = get_platform_endianess())
    {
        // Check if in range
        a_util::result::Result result_code = checkForInvalidArguments(start_bit, bit_length, sizeof(T));
        if (result_code != a_util::result::SUCCESS)
        {
            return result_code;
        }

        // Call template function
        detail::Converter<T, std::is_signed<T>::value,
            std::is_floating_point<T>::value>
            ::write(_buffer, start_bit, bit_length, value, endianess);

        return a_util::result::SUCCESS;
    }

private:
    /// internal buffer
    uint8_t *_buffer;
    /// size of internal buffer in bytes
    size_t _buffer_bytes;
    /// size of internal buffer in bits
    size_t _buffer_bits;

    /**
    * Check if the parameters for the reading and writing access are valid.
    * The variable to read from or into might be too small and the accessed region of the memory buffer
    * might be out of range.
    *
    * @param [in]  start_bit     Bit position to start reading from. The least significant bit
    *                            has the index 0.
    * @param [in]  bit_length    Number of bits to read.
    * @param [in]  size_variable Size of the variable to read into or write from.
    *
    * @return Returns a standard result code.
    */
    a_util::result::Result checkForInvalidArguments(size_t start_bit, size_t bit_length, size_t size_variable)
    {
        if (!_buffer)
        {
            return ERR_POINTER;
        }

        // Check invalid starting point
        if (start_bit >= _buffer_bits)
        {
            return ERR_INVALID_ARG;
        }

        // Check out of buffer bounds or length < 1
        if ((bit_length < 1)
            || (_buffer_bits < start_bit + bit_length))
        {
            return ERR_INVALID_ARG;
        }

        // Check variable size
        if (size_variable * 8 < bit_length)
        {
            return ERR_INVALID_ARG;
        }

        return a_util::result::SUCCESS;
    }

};

} // namespace memory
} // namespace a_util

#endif // A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED