/*****************************************************************************/
// Copyright 2006-2019 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/

#include <new>

#include "dng_ref_counted_block.h"

#include "dng_exceptions.h"

/*****************************************************************************/

dng_ref_counted_block::dng_ref_counted_block ()

	:	fBuffer (NULL)

	{

	}

/*****************************************************************************/

dng_ref_counted_block::dng_ref_counted_block (uint32 size)

	:	fBuffer (NULL)

	{

	Allocate (size);

	}

/*****************************************************************************/

dng_ref_counted_block::~dng_ref_counted_block ()
	{

	Clear ();

	}

/*****************************************************************************/

void dng_ref_counted_block::Allocate (uint32 size)
	{

	Clear ();

	if (size)
		{

		fBuffer = malloc (size + sizeof (header));

		if (!fBuffer)
			{

			ThrowMemoryFull ();

			}

		new (fBuffer) header (size);

		}

	}

/*****************************************************************************/

void dng_ref_counted_block::Clear ()
	{

	if (fBuffer)
		{

		bool doFree = false;

		header *blockHeader = (struct header *)fBuffer;

			{

			dng_lock_std_mutex lock (blockHeader->fMutex);

			if (--blockHeader->fRefCount == 0)
				doFree = true;

			}

		if (doFree)
			{

			blockHeader->~header ();

			free (fBuffer);

			}

		fBuffer = NULL;

		}

	}

/*****************************************************************************/

dng_ref_counted_block::dng_ref_counted_block (const dng_ref_counted_block &data)

	:   fBuffer (NULL)

	{

	header *blockHeader = (struct header *) data.fBuffer;

    if (blockHeader)
        {

		dng_lock_std_mutex lock (blockHeader->fMutex);

        blockHeader->fRefCount++;

        fBuffer = blockHeader;

        }

	}

/*****************************************************************************/

dng_ref_counted_block & dng_ref_counted_block::operator= (const dng_ref_counted_block &data)
	{

	if (this != &data)
		{

		Clear ();

		header *blockHeader = (struct header *) data.fBuffer;

        if (blockHeader)
            {

            dng_lock_std_mutex lock (blockHeader->fMutex);

            blockHeader->fRefCount++;

            fBuffer = blockHeader;

            }

		}

	return *this;

	}

/*****************************************************************************/

void dng_ref_counted_block::EnsureWriteable ()
	{

	if (fBuffer)
		{

		header *possiblySharedHeader = (header *) fBuffer;

			{

			dng_lock_std_mutex lock (possiblySharedHeader->fMutex);

			if (possiblySharedHeader->fRefCount > 1)
				{

				fBuffer = NULL;

				Allocate ((uint32)possiblySharedHeader->fSize);

				memcpy (Buffer (),
					((char *)possiblySharedHeader) + sizeof (struct header), // could just do + 1 w/o cast, but this makes the type mixing more explicit
					possiblySharedHeader->fSize);

				possiblySharedHeader->fRefCount--;

				}

			}

		}

	}

/*****************************************************************************/
