@@ -631,7 +631,7 @@ class Allocator
631
631
public:
632
632
Allocator () {};
633
633
Allocator (const size_t capacity)
634
- : capacity {capacity}
634
+ : capacity {capacity}, bytesRemaining {capacity}
635
635
{
636
636
data = TO_BYTES (calloc (1 , sizeof (BlockHeader) + sizeof (BlockFooter) + capacity));
637
637
@@ -672,7 +672,9 @@ class Allocator
672
672
void * Allocate (size_t size)
673
673
{
674
674
size_t requiredSize = sizeof (BlockHeader) + size + sizeof (BlockFooter);
675
- if (!data || capacity == 0 ||size == 0 || requiredSize < size) return nullptr ;
675
+ if (!data || capacity == 0 ||size == 0
676
+ || requiredSize < size || requiredSize > bytesRemaining) return nullptr ;
677
+
676
678
if (requiredSize < MIN_ALLOC_SIZE) return nullptr ;
677
679
678
680
size_t freeListIdx = 0 , fl = 0 , sl = 0 ;
@@ -694,13 +696,16 @@ class Allocator
694
696
BlockFooter* footer = TO_FBLOCK ((ptr+size));
695
697
footer->totalBlockSize = requiredSize;
696
698
699
+ bytesRemaining -= requiredSize;
700
+
697
701
return ptr;
698
702
}
699
703
700
704
template <typename T>
701
705
void Deallocate (T*& ptr)
702
706
{
703
707
if (!ptr) return ;
708
+ if ((TO_BYTES (ptr) < data || TO_BYTES (ptr) >= (data + capacity))) return ;
704
709
705
710
BlockHeader* header = TO_HBLOCK ((TO_BYTES (ptr) - sizeof (BlockHeader)));
706
711
size_t blockSize = GetHeaderSize (header);
@@ -716,6 +721,9 @@ class Allocator
716
721
if (nextHeader) nextHeader->sizeAndFlags |= IS_PREV_FREE_FLAG;
717
722
718
723
CreateNewBlock (TO_BYTES (targetHeader), totalBlockSize);
724
+
725
+ bytesRemaining += blockSize;
726
+
719
727
ptr = nullptr ;
720
728
}
721
729
@@ -755,7 +763,7 @@ class Allocator
755
763
BlockHeader* TryCoalesce (BlockHeader* header, OUT size_t & size)
756
764
{
757
765
BlockHeader* start = header;
758
- BlockHeader* prev = GetPreviousHeader (header );
766
+ BlockHeader* prev = GetPreviousHeader (start );
759
767
760
768
if (prev && BlockIsFree (prev))
761
769
{
@@ -775,11 +783,11 @@ class Allocator
775
783
return start;
776
784
}
777
785
778
- BlockHeader* GetPreviousHeader (void * currentHeader)
786
+ BlockHeader* GetPreviousHeader (BlockHeader * currentHeader)
779
787
{
780
788
if (IsHead (currentHeader)) return nullptr ;
781
- BlockFooter* prev = TO_FBLOCK (TO_BYTES (currentHeader) - sizeof (BlockFooter));
782
- BlockHeader* prevHeader = TO_HBLOCK (TO_BYTES (currentHeader) - prev->totalBlockSize );
789
+ BlockFooter* prev = TO_FBLOCK (( TO_BYTES (currentHeader) - sizeof (BlockFooter) ));
790
+ BlockHeader* prevHeader = TO_HBLOCK (( TO_BYTES (currentHeader) - prev->totalBlockSize ) );
783
791
return prevHeader;
784
792
}
785
793
@@ -791,7 +799,7 @@ class Allocator
791
799
return TO_HBLOCK (next);
792
800
}
793
801
794
- void TrySetPreviousHeaderFlag (void * currentHeader, size_t & flags)
802
+ void TrySetPreviousHeaderFlag (BlockHeader * currentHeader, size_t & flags)
795
803
{
796
804
BlockHeader* prevHeader = GetPreviousHeader (currentHeader);
797
805
if (prevHeader &&
@@ -842,6 +850,8 @@ class Allocator
842
850
843
851
size_t sizeFlag = (size << FLAG_BITS);
844
852
853
+ // 0000 0000 0000 0000
854
+
845
855
BlockHeader* header = TO_HBLOCK (ptr);
846
856
header->sizeAndFlags = (sizeFlag | IS_FREE_FLAG);
847
857
@@ -886,9 +896,11 @@ class Allocator
886
896
const uint64_t & FlBitmask () { return flBitmask; }
887
897
uint16_t *& SlBitmask () { return slBitmasks; }
888
898
FreeBlockNode** FreeList () { return freeList; }
899
+ const size_t BytesRemaining () { return bytesRemaining; }
889
900
890
901
private:
891
902
size_t capacity {0 };
903
+ size_t bytesRemaining {0 };
892
904
unsigned char * data {nullptr };
893
905
FreeBlockNode** freeList {nullptr };
894
906
@@ -909,6 +921,7 @@ UTEST(test_ResourceSystem, TestAllocatorCreationWithSize)
909
921
Allocator a (64 );
910
922
ASSERT_NE (a.Data (), nullptr );
911
923
ASSERT_EQ (a.Capacity (), 64 );
924
+ ASSERT_EQ (64 , a.BytesRemaining ());
912
925
913
926
// 0001 0000
914
927
ASSERT_EQ (a.FlBitmask (), 4 );
@@ -934,6 +947,7 @@ UTEST(test_ResourceSystem, TestAllocateFunction)
934
947
Allocator a (64 );
935
948
ASSERT_NE (a.Data (), nullptr );
936
949
ASSERT_EQ (a.Capacity (), 64 );
950
+ ASSERT_EQ (64 , a.BytesRemaining ());
937
951
938
952
TestStruct* p = (TestStruct*) a.Allocate (sizeof (TestStruct));
939
953
@@ -957,6 +971,7 @@ UTEST(test_ResourceSystem, TestAllocateFunction)
957
971
ASSERT_EQ (192 , header->sizeAndFlags );
958
972
ASSERT_EQ (data, p);
959
973
ASSERT_EQ (sizeof (BlockHeader) + sizeof (TestStruct) + sizeof (BlockFooter), footer->totalBlockSize );
974
+ ASSERT_EQ (40 , a.BytesRemaining ());
960
975
961
976
Siege::String* str = (Siege::String*) a.Allocate (sizeof (Siege::String));
962
977
@@ -974,9 +989,10 @@ UTEST(test_ResourceSystem, TestAllocateFunction)
974
989
auto newData = (Siege::String*) (((unsigned char *)newHeader) + sizeof (BlockHeader));
975
990
auto newFooter = (BlockFooter*) (((unsigned char *)newData) + sizeof (TestStruct));
976
991
977
- ASSERT_EQ (194 , newHeader->sizeAndFlags );
992
+ ASSERT_EQ (192 , newHeader->sizeAndFlags );
978
993
ASSERT_EQ (newData, str);
979
994
ASSERT_EQ (sizeof (BlockHeader) + sizeof (Siege::String) + sizeof (BlockFooter), newFooter->totalBlockSize );
995
+ ASSERT_EQ (16 , a.BytesRemaining ());
980
996
981
997
// Edge cases
982
998
@@ -994,13 +1010,21 @@ UTEST(test_ResourceSystem, TestAllocateFunction)
994
1010
Allocator overflowAlloc (SIZE_T_MAX);
995
1011
void * overflowPtr = overflowAlloc.Allocate (SIZE_T_MAX);
996
1012
ASSERT_FALSE (overflowPtr);
1013
+
1014
+ void * tooLargeCase = a.Allocate (100 );
1015
+ ASSERT_FALSE (tooLargeCase);
1016
+
1017
+ Allocator tooSmallAlloc (64 );
1018
+ void * tooLargeAlloc = a.Allocate (100 );
1019
+ ASSERT_FALSE (tooLargeCase);
997
1020
}
998
1021
999
1022
UTEST (test_ResourceSystem, TestDeallocateFunction)
1000
1023
{
1001
1024
Allocator a (64 );
1002
1025
ASSERT_NE (a.Data (), nullptr );
1003
1026
ASSERT_EQ (a.Capacity (), 64 );
1027
+ ASSERT_EQ (64 , a.BytesRemaining ());
1004
1028
1005
1029
TestStruct* p = (TestStruct*) a.Allocate (sizeof (TestStruct));
1006
1030
@@ -1016,6 +1040,7 @@ UTEST(test_ResourceSystem, TestDeallocateFunction)
1016
1040
FreeBlockNode* block = a.FreeList ()[(1 * 16 ) + 4 ];
1017
1041
ASSERT_EQ (block->next , nullptr );
1018
1042
ASSERT_EQ (block->prev , nullptr );
1043
+ ASSERT_EQ (40 , a.BytesRemaining ());
1019
1044
1020
1045
auto header = (BlockHeader*)a.Data ();
1021
1046
auto data = (TestStruct*) (((unsigned char *)header) + sizeof (BlockHeader));
@@ -1033,6 +1058,7 @@ UTEST(test_ResourceSystem, TestDeallocateFunction)
1033
1058
ASSERT_FALSE (p);
1034
1059
ASSERT_EQ (a.FlBitmask (), 4 );
1035
1060
ASSERT_EQ (a.SlBitmask ()[2 ], 1 );
1061
+ ASSERT_EQ (64 , a.BytesRemaining ());
1036
1062
1037
1063
p = (TestStruct*) a.Allocate (sizeof (TestStruct));
1038
1064
p->inta = 10 ;
@@ -1047,6 +1073,7 @@ UTEST(test_ResourceSystem, TestDeallocateFunction)
1047
1073
block = a.FreeList ()[(1 * 16 ) + 4 ];
1048
1074
ASSERT_EQ (block->next , nullptr );
1049
1075
ASSERT_EQ (block->prev , nullptr );
1076
+ ASSERT_EQ (40 , a.BytesRemaining ());
1050
1077
1051
1078
header = (BlockHeader*)a.Data ();
1052
1079
data = (TestStruct*) (((unsigned char *)header) + sizeof (BlockHeader));
@@ -1067,12 +1094,13 @@ UTEST(test_ResourceSystem, TestDeallocateFunction)
1067
1094
FreeBlockNode* NewBlock = a.FreeList ()[0 ];
1068
1095
ASSERT_EQ (NewBlock->next , nullptr );
1069
1096
ASSERT_EQ (NewBlock->prev , nullptr );
1097
+ ASSERT_EQ (16 , a.BytesRemaining ());
1070
1098
1071
1099
auto newHeader = (BlockHeader*)(TO_BYTES (footer) + sizeof (BlockFooter));
1072
1100
auto newData = (Siege::String*) (((unsigned char *)newHeader) + sizeof (BlockHeader));
1073
1101
auto newFooter = (BlockFooter*) (((unsigned char *)newData) + sizeof (TestStruct));
1074
1102
1075
- ASSERT_EQ (194 , newHeader->sizeAndFlags );
1103
+ ASSERT_EQ (192 , newHeader->sizeAndFlags );
1076
1104
ASSERT_EQ (newData, str);
1077
1105
ASSERT_EQ (sizeof (BlockHeader) + sizeof (Siege::String) + sizeof (BlockFooter), newFooter->totalBlockSize );
1078
1106
@@ -1084,10 +1112,127 @@ UTEST(test_ResourceSystem, TestDeallocateFunction)
1084
1112
FreeBlockNode* newFreeBlock = TO_FREEBLOCK ((TO_BYTES (header)+sizeof (BlockHeader)));
1085
1113
ASSERT_EQ (newFreeBlock->next , nullptr );
1086
1114
ASSERT_EQ (newFreeBlock->prev , nullptr );
1115
+ ASSERT_EQ (40 , a.BytesRemaining ());
1087
1116
1088
1117
a.Deallocate (str);
1089
- // ASSERT_FALSE(str);
1090
- // ASSERT_EQ(513, header->sizeAndFlags);
1091
- // ASSERT_EQ(a.FlBitmask(), 4);
1092
- // ASSERT_EQ(a.SlBitmask()[2], 1);
1118
+ ASSERT_FALSE (str);
1119
+ ASSERT_EQ (513 , header->sizeAndFlags );
1120
+ ASSERT_EQ (4 , a.FlBitmask ());
1121
+ ASSERT_EQ (1 , a.SlBitmask ()[2 ]);
1122
+ ASSERT_EQ (64 , a.BytesRemaining ());
1123
+ }
1124
+
1125
+ UTEST (test_ResourceSystem, TestBlockCoalescing)
1126
+ {
1127
+ Allocator a (128 );
1128
+ ASSERT_NE (a.Data (), nullptr );
1129
+ ASSERT_EQ (a.Capacity (), 128 );
1130
+ ASSERT_EQ (128 , a.BytesRemaining ());
1131
+
1132
+ TestStruct* p = (TestStruct*) a.Allocate (sizeof (TestStruct));
1133
+
1134
+ p->inta = 10 ;
1135
+ p->intb = 20 ;
1136
+
1137
+ ASSERT_EQ (10 , p->inta );
1138
+ ASSERT_EQ (20 , p->intb );
1139
+
1140
+ ASSERT_EQ (4 , a.FlBitmask ());
1141
+ ASSERT_EQ (0 , a.SlBitmask ()[1 ]);
1142
+
1143
+ FreeBlockNode* block = a.FreeList ()[42 ];
1144
+ ASSERT_EQ (block->next , nullptr );
1145
+ ASSERT_EQ (block->prev , nullptr );
1146
+ ASSERT_EQ (104 , a.BytesRemaining ());
1147
+
1148
+ auto header = (BlockHeader*)a.Data ();
1149
+ auto data = (TestStruct*) (((unsigned char *)header) + sizeof (BlockHeader));
1150
+ auto footer = (BlockFooter*) (((unsigned char *)data) + sizeof (TestStruct));
1151
+
1152
+ ASSERT_EQ (192 , header->sizeAndFlags );
1153
+ ASSERT_EQ (data, p);
1154
+ ASSERT_EQ (sizeof (BlockHeader) + sizeof (TestStruct) + sizeof (BlockFooter), footer->totalBlockSize );
1155
+
1156
+ Siege::String* str = (Siege::String*) a.Allocate (sizeof (Siege::String));
1157
+
1158
+ *str = " Hello There!" ;
1159
+ ASSERT_STREQ (str->Str ()," Hello There!" );
1160
+
1161
+ ASSERT_EQ (4 , a.FlBitmask ());
1162
+ ASSERT_EQ (0 , a.SlBitmask ()[1 ]);
1163
+
1164
+ FreeBlockNode* NewBlock = a.FreeList ()[36 ];
1165
+ ASSERT_EQ (NewBlock->next , nullptr );
1166
+ ASSERT_EQ (NewBlock->prev , nullptr );
1167
+ ASSERT_EQ (80 , a.BytesRemaining ());
1168
+
1169
+ auto newHeader = (BlockHeader*)(TO_BYTES (footer) + sizeof (BlockFooter));
1170
+ auto newData = (Siege::String*) (((unsigned char *)newHeader) + sizeof (BlockHeader));
1171
+ auto newFooter = (BlockFooter*) (((unsigned char *)newData) + sizeof (TestStruct));
1172
+
1173
+ ASSERT_EQ (192 , newHeader->sizeAndFlags );
1174
+ ASSERT_EQ (newData, str);
1175
+ ASSERT_EQ (sizeof (BlockHeader) + sizeof (Siege::String) + sizeof (BlockFooter), newFooter->totalBlockSize );
1176
+
1177
+ TestStruct* p2 = (TestStruct*) a.Allocate (sizeof (TestStruct));
1178
+
1179
+ p2->inta = 10 ;
1180
+ p2->intb = 20 ;
1181
+
1182
+ ASSERT_EQ (10 , p2->inta );
1183
+ ASSERT_EQ (20 , p2->intb );
1184
+
1185
+ ASSERT_EQ (a.FlBitmask (), 2 );
1186
+ ASSERT_EQ (4096 , a.SlBitmask ()[1 ]);
1187
+
1188
+ FreeBlockNode* newFreeBlock = a.FreeList ()[28 ];
1189
+ ASSERT_EQ (newFreeBlock->next , nullptr );
1190
+ ASSERT_EQ (newFreeBlock->prev , nullptr );
1191
+ ASSERT_EQ (56 , a.BytesRemaining ());
1192
+
1193
+ auto newHeader2 = (BlockHeader*)(TO_BYTES (newFooter) + sizeof (BlockFooter));
1194
+ auto newData2 = (TestStruct*) (((unsigned char *)newHeader2) + sizeof (BlockHeader));
1195
+ auto newFooter2 = (BlockFooter*) (((unsigned char *)newData2) + sizeof (TestStruct));
1196
+
1197
+ ASSERT_EQ (192 , header->sizeAndFlags );
1198
+ ASSERT_EQ (data, p);
1199
+ ASSERT_EQ (sizeof (BlockHeader) + sizeof (TestStruct) + sizeof (BlockFooter), footer->totalBlockSize );
1200
+
1201
+ a.Deallocate (p);
1202
+
1203
+ FreeBlockNode* firstFree = a.FreeList ()[8 ];
1204
+ ASSERT_EQ (firstFree->next , nullptr );
1205
+ ASSERT_EQ (firstFree->prev , nullptr );
1206
+ ASSERT_EQ (80 , a.BytesRemaining ());
1207
+
1208
+ BlockHeader* firstFreeHeader = TO_HBLOCK ((TO_BYTES (firstFree) - sizeof (BlockHeader)));
1209
+ ASSERT_TRUE (firstFreeHeader->sizeAndFlags & IS_FREE_FLAG);
1210
+ ASSERT_FALSE (firstFreeHeader->sizeAndFlags & IS_PREV_FREE_FLAG);
1211
+ ASSERT_FALSE (p);
1212
+ ASSERT_EQ (a.FlBitmask (), 3 );
1213
+ ASSERT_EQ (256 , a.SlBitmask ()[0 ]);
1214
+
1215
+ a.Deallocate (p2);
1216
+ ASSERT_FALSE (p2);
1217
+ ASSERT_EQ (104 , a.BytesRemaining ());
1218
+ ASSERT_EQ (a.FlBitmask (), 5 );
1219
+ ASSERT_EQ (256 , a.SlBitmask ()[0 ]);
1220
+ ASSERT_EQ (16 , a.SlBitmask ()[2 ]);
1221
+
1222
+ a.Deallocate (str);
1223
+ ASSERT_FALSE (str);
1224
+ ASSERT_EQ (128 , a.BytesRemaining ());
1225
+ ASSERT_EQ (a.FlBitmask (), 8 );
1226
+ ASSERT_EQ (a.SlBitmask ()[3 ], 1 );
1227
+
1228
+ // Edge Cases
1229
+
1230
+ void * badPointer = nullptr ;
1231
+ a.Deallocate (badPointer);
1232
+ ASSERT_FALSE (badPointer);
1233
+
1234
+ // try to deallocate pointer not in allocator;
1235
+ uint64_t * val = new uint64_t ;
1236
+
1237
+ a.Deallocate (val);
1093
1238
}
0 commit comments