@@ -561,3 +561,282 @@ UTEST_F(test_ResourceSystem, LoadAnimationData)
561
561
}
562
562
}
563
563
}
564
+ struct TestStruct
565
+ {
566
+ uint64_t inta {0 };
567
+ uint64_t intb {0 };
568
+ };
569
+
570
+ struct FreeBlock
571
+ {
572
+ size_t size {0 };
573
+ FreeBlock* prev {nullptr };
574
+ FreeBlock* next {nullptr };
575
+ };
576
+
577
+ struct SmallBlockHeader
578
+ {
579
+ uint16_t sizeAndFlags {0 };
580
+ };
581
+
582
+ struct SmallBlockFooter
583
+ {
584
+ uint16_t sizeAndFlags {0 };
585
+ };
586
+
587
+ struct BlockHeader
588
+ {
589
+ uint32_t sizeAndFlags {0 };
590
+ };
591
+
592
+ struct BlockFooter
593
+ {
594
+ uint32_t sizeAndFlags {0 };
595
+ };
596
+
597
+ struct LargeBlockHeader
598
+ {
599
+ uint64_t sizeAndFlags {0 };
600
+ };
601
+
602
+ struct LargeBlockFooter
603
+ {
604
+ uint64_t sizeAndFlags {0 };
605
+ };
606
+
607
+ #define FL_MIN 4
608
+ #define FL_INDEX (capacity ) 63 - __builtin_clzll(capacity)
609
+ #define MAX_SL_INDEX 16
610
+
611
+ #define SL_BITS 4 // we get the log(2) the max sl index
612
+ #define SL_MASK ((1 << SL_BITS) - 1 )
613
+
614
+ #define INVALID_INDEX SIZE_T_MAX
615
+
616
+ class Allocator
617
+ {
618
+ public:
619
+ Allocator () {};
620
+ Allocator (const size_t capacity)
621
+ : capacity {capacity}
622
+ {
623
+ data = reinterpret_cast <unsigned char *>(calloc (sizeof (FreeBlock), capacity));
624
+
625
+ // __builtin_clzll calculates the leading zeroes of a 64 bit integer. To find the furthest
626
+ // bit set to 1 we need to minus 63 from the result (since a 64 bit integer has a bit range
627
+ // of 0 - 63)
628
+ uint64_t rawFl = FL_INDEX (capacity);
629
+ uint64_t maxIndices = rawFl - FL_MIN + 1 ;
630
+
631
+ freeList = reinterpret_cast <FreeBlock**>(calloc (maxIndices * MAX_SL_INDEX, sizeof (FreeBlock*)));
632
+ slBitmasks = reinterpret_cast <uint16_t *>(calloc (maxIndices, sizeof (uint16_t )));
633
+
634
+ FreeBlock* firstBlock = reinterpret_cast <FreeBlock*>(data);
635
+ firstBlock->size = capacity;
636
+
637
+ size_t offsetFlIdx = rawFl - FL_MIN;
638
+
639
+ size_t sl = SlIndex (capacity, rawFl);
640
+ size_t freeListIdx = (offsetFlIdx) * MAX_SL_INDEX + sl;
641
+
642
+ freeList[freeListIdx] = firstBlock;
643
+
644
+ flBitmask |= 1ULL << (offsetFlIdx);
645
+ slBitmasks[offsetFlIdx] |= 1ULL << sl;
646
+ }
647
+
648
+ ~Allocator ()
649
+ {
650
+ if (data) free (data);
651
+ if (freeList) free (freeList);
652
+ if (slBitmasks) free (slBitmasks);
653
+
654
+ capacity = 0 ;
655
+ data = nullptr ;
656
+ freeList = nullptr ;
657
+ slBitmasks = nullptr ;
658
+ }
659
+
660
+ void * Allocate (size_t size)
661
+ {
662
+ if (size < 16 ) return nullptr ;
663
+
664
+ size_t rawFl = FL_INDEX (size);
665
+ size_t sl = SlIndex (size, rawFl);
666
+ size_t fl = rawFl - FL_MIN;
667
+
668
+ size_t freeListIdx = 0 ;
669
+
670
+ freeListIdx = fl * MAX_SL_INDEX + sl;
671
+
672
+ size_t freeSlotFl = fl;
673
+ size_t freeSlotSl = sl;
674
+
675
+ if (!IsFree (fl, sl)) freeListIdx = GetNextFreeSlotIndex (freeSlotFl, freeSlotSl);
676
+ if (freeListIdx == INVALID_INDEX) return nullptr ;
677
+
678
+ FreeBlock* block = freeList[freeListIdx];
679
+
680
+ size_t requiredSize = sizeof (BlockHeader) + size + sizeof (BlockFooter);
681
+
682
+ // Needs to be split
683
+ if (block->size > requiredSize)
684
+ {
685
+ slBitmasks[freeSlotFl] &= ~(1 << freeSlotSl);
686
+ if (!slBitmasks[freeSlotFl]) flBitmask &= ~(1 << freeSlotFl);
687
+
688
+ size_t newSize = block->size - requiredSize;
689
+ size_t newRawFl = FL_INDEX (newSize);
690
+ size_t newFl = newRawFl - FL_MIN;
691
+ size_t newSl = SlIndex (newSize, newRawFl);
692
+ size_t newFreeListIdx = newFl * MAX_SL_INDEX + newSl;
693
+
694
+ FreeBlock* newBlock = reinterpret_cast <FreeBlock*>(reinterpret_cast <unsigned char *>(block) + requiredSize);
695
+
696
+ newBlock->size = newSize;
697
+ newBlock->prev = nullptr ;
698
+ newBlock->next = nullptr ;
699
+
700
+ freeList[newFreeListIdx] = newBlock;
701
+
702
+ flBitmask |= 1ULL << (newFl);
703
+ slBitmasks[newFl] |= 1ULL << newSl;
704
+ }
705
+
706
+ BlockHeader* header = reinterpret_cast <BlockHeader*>(reinterpret_cast <unsigned char *>(block));
707
+ size_t flags = 0 ;
708
+ flags |= BitMask (0 );
709
+
710
+ BlockFooter* prev = reinterpret_cast <BlockFooter*>(reinterpret_cast <unsigned char *>(header) - sizeof (BlockFooter));
711
+ BlockHeader* prevHeader = reinterpret_cast <BlockHeader*>(reinterpret_cast <unsigned char *>(prev) - prev->sizeAndFlags - sizeof (BlockHeader));
712
+
713
+ if ((unsigned char *)header != data && !Mask (prevHeader->sizeAndFlags ,0 )) flags |= BitMask (1 );
714
+
715
+ header->sizeAndFlags = (requiredSize << 3 ) | flags;
716
+
717
+ unsigned char * ptr = reinterpret_cast <unsigned char *>(reinterpret_cast <unsigned char *>(header)+sizeof (BlockHeader));
718
+
719
+ BlockFooter* footer = reinterpret_cast <BlockFooter*>(ptr+size);
720
+ footer->sizeAndFlags = size;
721
+
722
+ return ptr;
723
+ }
724
+
725
+ size_t GetNextFreeSlotIndex (OUT size_t & fl,OUT size_t & sl)
726
+ {
727
+ sl = FindLargerSlots (slBitmasks[fl], sl);
728
+ if (sl) return fl * MAX_SL_INDEX + __builtin_ctz (sl);
729
+
730
+ fl = FindLargerSlots64 (flBitmask, fl);
731
+
732
+ if (!fl) return INVALID_INDEX;
733
+
734
+ fl = __builtin_ctzll (fl);
735
+ CC_ASSERT (slBitmasks[fl] > 0 ,
736
+ " SlBitmasks is returning 0. This should not be happening and indicates an implementation error." )
737
+
738
+ sl = __builtin_ctz (slBitmasks[fl]);
739
+
740
+ return fl * MAX_SL_INDEX + sl;
741
+ }
742
+
743
+ bool IsFree (size_t fl, size_t sl) { return Mask (slBitmasks[fl], BitMask (sl)); }
744
+ const size_t FindLargerSlots (size_t mask, size_t idx) { return Mask (mask, FlipBits (BitMask (idx+1 )-1 )); }
745
+ const size_t FindLargerSlots64 (size_t mask, size_t idx) { return Mask (mask, FlipBits (BitMask64 (idx+1 )-1 )); }
746
+ const size_t BitMask (size_t shiftAmount) { return 1 << shiftAmount; }
747
+ const size_t BitMask64 (size_t shiftAmount) { return 1ULL << shiftAmount; }
748
+ const size_t FlipBits (size_t mask) { return ~(mask); }
749
+ const size_t Mask (size_t value, size_t mask) { return value & mask; }
750
+
751
+
752
+ size_t & Capacity () { return capacity;}
753
+ unsigned char * Data () { return data; };
754
+ size_t SlIndex (size_t size, size_t fl) { return (size >> (fl - SL_BITS)) & SL_MASK; }
755
+ const uint64_t & FlBitmask () { return flBitmask; }
756
+ uint16_t *& SlBitmask () { return slBitmasks; }
757
+ FreeBlock** FreeList () { return freeList; }
758
+
759
+ private:
760
+ size_t capacity {0 };
761
+ unsigned char * data {nullptr };
762
+ FreeBlock** freeList {nullptr };
763
+
764
+ // bitmasks
765
+ uint64_t flBitmask {0 };
766
+ uint16_t * slBitmasks {nullptr };
767
+ };
768
+
769
+ UTEST (test_ResourceSystem, TestEmptyAllocatorCreation)
770
+ {
771
+ Allocator a;
772
+ ASSERT_EQ (a.Data (), nullptr );
773
+ ASSERT_EQ (a.Capacity (), 0 );
774
+ }
775
+
776
+ UTEST (test_ResourceSystem, TestAllocatorCreationWithSize)
777
+ {
778
+ Allocator a (64 );
779
+ ASSERT_NE (a.Data (), nullptr );
780
+ ASSERT_EQ (a.Capacity (), 64 );
781
+
782
+ // 0001 0000
783
+ ASSERT_EQ (a.FlBitmask (), 4 );
784
+ ASSERT_EQ (a.SlBitmask ()[2 ], 1 );
785
+
786
+ FreeBlock* block = a.FreeList ()[2 * 16 ];
787
+ ASSERT_EQ (block->size ,64 );
788
+ ASSERT_EQ (block->next , nullptr );
789
+ ASSERT_EQ (block->prev , nullptr );
790
+ }
791
+
792
+ UTEST (test_ResourceSystem, TestAllocateFunction)
793
+ {
794
+ Allocator a (64 );
795
+ ASSERT_NE (a.Data (), nullptr );
796
+ ASSERT_EQ (a.Capacity (), 64 );
797
+
798
+ TestStruct* p = (TestStruct*) a.Allocate (sizeof (TestStruct));
799
+
800
+ p->inta = 10 ;
801
+ p->intb = 20 ;
802
+
803
+ ASSERT_EQ (10 , p->inta );
804
+ ASSERT_EQ (20 , p->intb );
805
+
806
+ ASSERT_EQ (a.FlBitmask (), 2 );
807
+ ASSERT_EQ (16 , a.SlBitmask ()[1 ]);
808
+
809
+ FreeBlock* block = a.FreeList ()[(1 * 16 ) + 4 ];
810
+ ASSERT_EQ (block->size ,40 );
811
+ ASSERT_EQ (block->next , nullptr );
812
+ ASSERT_EQ (block->prev , nullptr );
813
+
814
+ auto header = (BlockHeader*)a.Data ();
815
+ auto data = (TestStruct*) (((unsigned char *)header) + sizeof (BlockHeader));
816
+ auto footer = (BlockFooter*) (((unsigned char *)data) + sizeof (TestStruct));
817
+
818
+ ASSERT_EQ (193 , header->sizeAndFlags );
819
+ ASSERT_EQ (data, p);
820
+ ASSERT_EQ (sizeof (TestStruct), footer->sizeAndFlags );
821
+
822
+ Siege::String* str = (Siege::String*) a.Allocate (sizeof (Siege::String));
823
+
824
+ *str = " Hello There!" ;
825
+ ASSERT_STREQ (str->Str ()," Hello There!" );
826
+
827
+ ASSERT_EQ (a.FlBitmask (), 1 );
828
+ ASSERT_EQ (0 , a.SlBitmask ()[1 ]);
829
+
830
+ FreeBlock* NewBlock = a.FreeList ()[0 ];
831
+ ASSERT_EQ (NewBlock->size ,16 );
832
+ ASSERT_EQ (NewBlock->next , nullptr );
833
+ ASSERT_EQ (NewBlock->prev , nullptr );
834
+
835
+ auto newHeader = (BlockHeader*)((unsigned char *)footer + sizeof (BlockFooter));
836
+ auto newData = (Siege::String*) (((unsigned char *)newHeader) + sizeof (BlockHeader));
837
+ auto newFooter = (BlockFooter*) (((unsigned char *)newData) + sizeof (TestStruct));
838
+
839
+ ASSERT_EQ (195 , newHeader->sizeAndFlags );
840
+ ASSERT_EQ (newData, str);
841
+ ASSERT_EQ (sizeof (Siege::String), newFooter->sizeAndFlags );
842
+ }
0 commit comments