summaryrefslogtreecommitdiff
path: root/Runtime/Containers/ConstantString.cpp
blob: 9cda5af858cd4ba812de9476d15d0f9f12760d98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "UnityPrefix.h"
#include "ConstantString.h"
#include "ConstantStringManager.h"
#include "Runtime/Threads/AtomicOps.h"

///@TODO: Use 24 bits for refcount
///@TODO: Handle ref count overflow

void ConstantString::create_empty ()
{
	cleanup();
	m_Buffer = GetConstantStringManager().GetEmptyString();
	Assert(!owns_string());
}

// The header compressed the label and refcount into a 32 bits.
// Refcount lives on the 0xFFFF, label lives on the higher bits.
// We use atomic ops for thread safety on refcounting when deleting ConstantStrings.
struct AllocatedStringHeader
{
	volatile int refCountAndLabel;
};

MemLabelId GetLabel (const AllocatedStringHeader& header)
{
	int intLabel = (header.refCountAndLabel & (0x0000FFFF)) << 16;
	ProfilerAllocationHeader* root = GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString);
	MemLabelId label( (MemLabelIdentifier)intLabel, root);
	return label;
}

void SetLabel (AllocatedStringHeader& header, MemLabelId label)
{
	int labelInt = label.label;
	Assert(labelInt < 0xFFFF);
	labelInt <<= 16;
	
	header.refCountAndLabel &= 0x0000FFFF;
	header.refCountAndLabel |= labelInt;
}

int GetRefCount (int refCountAndLabel)
{
	return refCountAndLabel & 0xFFFF;
}


inline static AllocatedStringHeader* GetHeader (const char* ptr)
{
	return reinterpret_cast<AllocatedStringHeader*> (const_cast<char*> (ptr) - sizeof(AllocatedStringHeader));
}

void ConstantString::operator = (const ConstantString& input)
{
	assign(input);
}

void ConstantString::assign (const ConstantString& input)
{
	cleanup();
	m_Buffer = input.m_Buffer;
	if (owns_string())
	{
		AtomicIncrement(&GetHeader (get_char_ptr_fast())->refCountAndLabel);
	}
}


void ConstantString::assign (const char* str, MemLabelId label)
{
	cleanup();
	const char* constantString = GetConstantStringManager().GetConstantString(str);
	// Own Strings
	if (constantString == NULL)
	{
		label.SetRootHeader(GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString));
		size_t length = strlen(str);

		char* allocated = (char*)UNITY_MALLOC (label, length + 1 + sizeof(AllocatedStringHeader));
		char* allocatedString = allocated + sizeof(AllocatedStringHeader);

		AllocatedStringHeader& header = *GetHeader (allocatedString);
		header.refCountAndLabel = 1;
		SetLabel(header, label);

		Assert(GetRefCount(header.refCountAndLabel) == 1);
		memcpy(allocatedString, str, length);
		allocatedString[length] = 0;
		
		
		m_Buffer = reinterpret_cast<char*> (reinterpret_cast<size_t> (allocatedString) | 1);
		Assert(owns_string());
	}
	else
	{
		m_Buffer = constantString;
		Assert(!owns_string());
	}
}

void ConstantString::cleanup ()
{
	if (owns_string())
	{
		AllocatedStringHeader* header = GetHeader(get_char_ptr_fast ());
		
		int newRefCount = AtomicDecrement (&header->refCountAndLabel);
		newRefCount = GetRefCount(newRefCount);
		
		if (newRefCount == 0)
		{
			MemLabelId label = GetLabel(*header);
			UNITY_FREE(label, header);
		}
	}
	
	m_Buffer = NULL;
}

ConstantString::~ConstantString ()
{
	cleanup ();
}