diff --git a/Package.swift b/Package.swift index d25a0873..aa86991c 100644 --- a/Package.swift +++ b/Package.swift @@ -29,6 +29,7 @@ let includePath = SDKPath.appending("/usr/lib/swift") var sharedCSettings: [CSetting] = [ .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), + .define("NDEBUG", .when(configuration: .release)), ] var sharedSwiftSettings: [SwiftSetting] = [ @@ -110,26 +111,54 @@ if warningsAsErrorsCondition { // MARK: - Targets +let openGraphTarget = Target.target( + name: "OpenGraph", + dependencies: ["OpenGraph_SPI"], + cSettings: sharedCSettings, + swiftSettings: sharedSwiftSettings +) +// FIXME: Merge into one target +// OpenGraph is a C++ & Swift mix target. +// The SwiftPM support for such usage is still in progress. +let openGraphSPITarget = Target.target( + name: "OpenGraph_SPI", + cSettings: sharedCSettings + [ + .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + ] +) let openGraphShimsTarget = Target.target( name: "OpenGraphShims", cSettings: sharedCSettings, swiftSettings: sharedSwiftSettings ) -let openGraphShimsTestTarget = Target.testTarget( - name: "OpenGraphShimsTests", +// MARK: - Test Targets + +let openGraphTestTarget = Target.testTarget( + name: "OpenGraphTests", dependencies: [ - "OpenGraphShims", + "OpenGraph", ], exclude: ["README.md"], cSettings: sharedCSettings, swiftSettings: sharedSwiftSettings ) - -let openGraphTestTarget = Target.testTarget( - name: "OpenGraphTests", +let openGraphSPITestTarget = Target.testTarget( + name: "OpenGraph_SPITests", dependencies: [ - "OpenGraph", + "OpenGraph_SPI", + ], + exclude: ["README.md"], + cSettings: sharedCSettings + [ + .headerSearchPath("../../Sources/OpenGraph_SPI"), + ], + swiftSettings: sharedSwiftSettings, + linkerSettings: [.linkedFramework("XCTest")] +) +let openGraphShimsTestTarget = Target.testTarget( + name: "OpenGraphShimsTests", + dependencies: [ + "OpenGraphShims", ], exclude: ["README.md"], cSettings: sharedCSettings, @@ -159,37 +188,24 @@ let openGraphSPICompatibilityTestTarget = Target.testTarget( let package = Package( name: "OpenGraph", products: [ - .library(name: "OpenGraph_SPI", targets: ["OpenGraph_SPI"]), - .library(name: "OpenGraph", targets: ["OpenGraph"]), - .library(name: "OpenGraphShims", targets: ["OpenGraphShims"]), + .library(name: "OpenGraph", type: .dynamic, targets: ["OpenGraph", "OpenGraph_SPI"]), + .library(name: "OpenGraphShims", type: .dynamic, targets: ["OpenGraph", "OpenGraph_SPI", "OpenGraphShims"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-numerics", from: "1.0.2"), ], targets: [ - // FIXME: Merge into one target - // OpenGraph is a C++ & Swift mix target. - // The SwiftPM support for such usage is still in progress. - .target( - name: "OpenGraph_SPI", - cSettings: sharedCSettings + [ - .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), - ] - ), - .target( - name: "OpenGraph", - dependencies: ["OpenGraph_SPI"], - cSettings: sharedCSettings, - swiftSettings: sharedSwiftSettings - ), + openGraphTarget, + openGraphSPITarget, openGraphShimsTarget, openGraphTestTarget, + openGraphSPITestTarget, openGraphShimsTestTarget, openGraphCompatibilityTestTarget, openGraphSPICompatibilityTestTarget, ], - cxxLanguageStandard: .cxx17 + cxxLanguageStandard: .cxx20 ) diff --git a/README.md b/README.md index 58be9580..3bf306df 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ The project is for the following purposes: Currently, this project is in early development. +## Credits + +OpenGraph_SPI's Data, Graph, Vector and more is modified based on [Compute](https://github.com/jcmosc/Compute)'s implementations. + ## License See LICENSE file - MIT diff --git a/Sources/OpenGraph_SPI/Data/page.hpp b/Sources/OpenGraph_SPI/Data/page.hpp new file mode 100644 index 00000000..22c2ce41 --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/page.hpp @@ -0,0 +1,27 @@ +// +// page.hpp +// OpenGraph_SPI + +#ifndef page_hpp +#define page_hpp + +#include "OGBase.h" +#include "ptr.hpp" + +namespace OG { +namespace data { + +class zone; +template class ptr; + +struct page { + zone *zone; + ptr previous; + uint32_t total; + uint32_t in_use; +}; /* page */ + +} /* data */ +} /* OG */ + +#endif /* page_hpp */ diff --git a/Sources/OpenGraph_SPI/Data/page_const.hpp b/Sources/OpenGraph_SPI/Data/page_const.hpp new file mode 100644 index 00000000..b03ea77c --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/page_const.hpp @@ -0,0 +1,28 @@ +// +// page_const.hpp +// OpenGraph_SPI + +#ifndef page_const_hpp +#define page_const_hpp + +#include "OGBase.h" + +namespace OG { +namespace data { + +constexpr const uint32_t page_mask_bits = 9; + +/// 0x200 +constexpr const uint32_t page_size = 1 << page_mask_bits; + +/// 0x1FF +constexpr const uint32_t page_mask = page_size - 1; + +/// 0xFFFF_FE00 +constexpr const uintptr_t page_alignment = ~page_mask; + +} /* data */ +} /* OG */ + + +#endif /* page_const_hpp */ diff --git a/Sources/OpenGraph_SPI/Data/ptr.hpp b/Sources/OpenGraph_SPI/Data/ptr.hpp new file mode 100644 index 00000000..b1b18322 --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/ptr.hpp @@ -0,0 +1,110 @@ +// +// ptr.hpp +// OpenGraph_SPI +// +// Status: Complete +// Modified from https://github.com/jcmosc/Compute/blob/0a6b21a4cdeb9bbdd95e7e914c4e18bed37a2456/Sources/ComputeCxx/Data/Pointer.h [MIT License] + +#ifndef ptr_hpp +#define ptr_hpp + +#include "OGBase.h" +#include "table.hpp" +#include +#include "page_const.hpp" + +OG_ASSUME_NONNULL_BEGIN + +namespace OG { +namespace data { + +struct page; + +template class ptr { +public: + using element_type = T; + using difference_type = uint32_t; + +private: + difference_type _offset; + + template friend class ptr; + +public: + OG_INLINE OG_CONSTEXPR ptr(difference_type offset = 0) : _offset(offset){}; + OG_INLINE OG_CONSTEXPR ptr(std::nullptr_t){}; + + OG_INLINE + void assert_valid(uint32_t capacity = shared_table().data_capacity()) const { + if (capacity <= _offset) { + precondition_failure("invalid data offset: %u", _offset); + } + } + + OG_INLINE + element_type *_Nonnull get(vm_address_t base = shared_table().data_base()) const OG_NOEXCEPT { + assert(_offset != 0); + return reinterpret_cast(base + _offset); + } + + OG_INLINE OG_CONSTEXPR + ptr page_ptr() const OG_NOEXCEPT { return ptr(_offset & page_alignment); } + + OG_INLINE OG_CONSTEXPR + uint32_t page_index() const OG_NOEXCEPT { return (_offset >> page_mask_bits) - 1; } + + OG_INLINE OG_CONSTEXPR + difference_type page_relative_offset() const OG_NOEXCEPT { return _offset & page_mask; } + + template ptr aligned(difference_type alignment_mask = sizeof(difference_type) - 1) const { + return ptr((_offset + alignment_mask) & ~alignment_mask); + }; + + OG_INLINE OG_CONSTEXPR + operator bool() const OG_NOEXCEPT { return _offset != 0; }; + + OG_INLINE OG_CONSTEXPR + std::add_lvalue_reference_t operator*() const OG_NOEXCEPT { return *get(); }; + + OG_INLINE OG_CONSTEXPR + T *_Nonnull operator->() const OG_NOEXCEPT { return get(); }; + + OG_INLINE OG_CONSTEXPR + bool operator==(std::nullptr_t) const OG_NOEXCEPT { return _offset == 0; }; + + OG_INLINE OG_CONSTEXPR + bool operator!=(std::nullptr_t) const OG_NOEXCEPT { return _offset != 0; }; + + OG_INLINE OG_CONSTEXPR + bool operator<(difference_type offset) const OG_NOEXCEPT { return _offset < offset; }; + + OG_INLINE OG_CONSTEXPR + bool operator<=(difference_type offset) const OG_NOEXCEPT { return _offset <= offset; }; + + OG_INLINE OG_CONSTEXPR + bool operator>(difference_type offset) const OG_NOEXCEPT { return _offset > offset; }; + + OG_INLINE OG_CONSTEXPR + bool operator>=(difference_type offset) const OG_NOEXCEPT { return _offset >= offset; }; + + template + OG_INLINE OG_CONSTEXPR + ptr operator+(difference_type shift) const OG_NOEXCEPT { return ptr(_offset + shift); }; + + template + OG_INLINE OG_CONSTEXPR + ptr operator-(difference_type shift) const OG_NOEXCEPT { return ptr(_offset - shift); }; + + template + OG_INLINE OG_CONSTEXPR + difference_type operator-(const ptr &other) const OG_NOEXCEPT { + return _offset - other._offset; + }; +}; /* ptr */ + +} /* data */ +} /* OG */ + +OG_ASSUME_NONNULL_END + +#endif /* ptr_hpp */ diff --git a/Sources/OpenGraph_SPI/Data/table.cpp b/Sources/OpenGraph_SPI/Data/table.cpp new file mode 100644 index 00000000..6a2877ac --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/table.cpp @@ -0,0 +1,279 @@ +// +// table.cpp +// OpenGraph_SPI +// +// Audited for iOS 18.0 +// Status: WIP +// Modified from https://github.com/jcmosc/Compute/blob/0a6b21a4cdeb9bbdd95e7e914c4e18bed37a2456/Sources/ComputeCxx/Data/Table.cpp [MIT License] + +#include "table.hpp" +#include "page.hpp" +#include "page_const.hpp" +#include "zone.hpp" +#include "../Util/assert.hpp" +#include +#include +#if OG_TARGET_OS_DARWIN +#include +#else +#include +#endif + +#if OG_TARGET_OS_DARWIN +#include +#endif + +void *OGGraphVMRegionBaseAddress; + +namespace OG { +namespace data { + +#if OG_TARGET_OS_DARWIN +malloc_zone_t *_Nullable _malloc_zone; +#endif + +table &table::ensure_shared() { + static dispatch_once_t once; + dispatch_once_f(&once, nullptr, [](void *_Nullable context){ + new (_shared_table_bytes) table(); + }); + return shared_table(); +} + +table::table() { + constexpr vm_size_t initial_size = 32 * pages_per_map * page_size; + _region_capacity = initial_size; + void *region = mmap(nullptr, initial_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + _region_base = reinterpret_cast(region); + OGGraphVMRegionBaseAddress = region; + if (region == MAP_FAILED) { + OG::precondition_failure("memory allocation failure (%u bytes, %u)", _region_capacity, errno); + } + _data_base = reinterpret_cast(region) - page_size; + _data_capacity = initial_size + page_size; + #if OG_TARGET_OS_DARWIN + if (_malloc_zone == nullptr) { + malloc_zone_t *zone = malloc_create_zone(0, 0); + _malloc_zone = zone; + malloc_set_zone_name(zone, "OpenGraph graph data"); + } + #else + precondition_failure("not implemented"); + #endif +} + +void table::grow_region() OG_NOEXCEPT { + #if OG_TARGET_OS_DARWIN + uint32_t new_size = 4 * _region_capacity; + // Check size does not exceed 32 bits + if (new_size <= _region_capacity) { + precondition_failure("exhausted data space"); + } + void *new_region = mmap(nullptr, new_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (new_region == MAP_FAILED) { + precondition_failure("memory allocation failure (%u bytes, %u)", new_size, errno); + } + + vm_prot_t cur_protection = VM_PROT_NONE; + vm_prot_t max_protection = VM_PROT_NONE; + kern_return_t error = vm_remap(mach_task_self(), + reinterpret_cast(&new_region), + _region_capacity, + 0, + VM_FLAGS_OVERWRITE, + mach_task_self(), + reinterpret_cast(_region_base), + false, + &cur_protection, + &max_protection, + VM_INHERIT_NONE); + if (error) { + precondition_failure("vm_remap failure: 0x%x", error); + } + _remapped_regions.push_back({this->region_base(), this->region_capacity()}); + OGGraphVMRegionBaseAddress = new_region; + _data_base = reinterpret_cast(new_region) - page_size; + _region_base = reinterpret_cast(new_region); + _region_capacity = new_size; + _data_capacity = new_size + page_size; + #else + precondition_failure("not implemented"); + #endif +} + +void table::make_pages_reusable(uint32_t page_index, bool reusable) OG_NOEXCEPT { + #if OG_TARGET_OS_DARWIN + static constexpr uint32_t mapped_pages_size = page_size * pages_per_map; // 64 * 512 = 0x8000 + void *mapped_pages_address = reinterpret_cast(region_base() + ((page_index * page_size) & ~(mapped_pages_size - 1))); + int advice = reusable ? MADV_FREE_REUSABLE : MADV_FREE_REUSE; + madvise(mapped_pages_address, mapped_pages_size, advice); + static bool unmap_reusable = []() -> bool { + char *result = getenv("OG_UNMAP_REUSABLE"); + if (result) { + return atoi(result) != 0; + } + return false; + }(); + if (unmap_reusable) { + int protection = reusable ? PROT_NONE : (PROT_READ | PROT_WRITE); + mprotect(mapped_pages_address, mapped_pages_size, protection); + } + _reusable_pages_num += reusable ? mapped_pages_size : -mapped_pages_size; + #else + precondition_failure("not implemented"); + #endif +} + +// TO BE AUDITED +ptr table::alloc_page(zone *zone, uint32_t needed_size) OG_NOEXCEPT { + lock(); + + uint32_t needed_pages = (needed_size + page_mask) / page_size; + + // assume we'll have to append a new page + uint32_t new_page_index = _page_maps.size() * pages_per_map; + + // scan for consecutive free pages + if (!_page_maps.empty() && _used_pages_num <= _page_maps.size() * pages_per_map) { + + uint32_t start_map_index = _map_search_start; + for (int i = 0; i < _page_maps.size(); i++) { + int map_index = start_map_index + i; + if (map_index >= _page_maps.size()) { + map_index -= _page_maps.size(); // wrap around + } + + page_map_type free_pages_map = _page_maps[map_index].flip(); + while (free_pages_map.any()) { + + int candidate_bit = std::countr_zero(static_cast(free_pages_map.to_ullong())); + + // scan ahead to find enough consecutive free pages + bool found = false; + if (needed_pages > 1) { + for (int j = 1; j < needed_pages; j++) { + int next_page_index = (map_index * pages_per_map) + candidate_bit + j; + int next_map_index = next_page_index / pages_per_map; + if (next_map_index == _page_maps.size()) { + // There are not enough maps, but the trailing pages are contiguous so this page is + // usable + found = true; + break; + } + if (_page_maps[next_map_index].test(next_page_index % pages_per_map)) { + // next page is used, remove this page from free_pages_map + free_pages_map.reset(candidate_bit); + break; + } + } + found = true; + } else { + // only need one page + found = true; + } + + if (found) { + new_page_index = (map_index * pages_per_map) + candidate_bit; + _map_search_start = map_index; + break; + } + } + } + } + + // update maps + for (int i = 0; i < needed_pages; i++) { + uint32_t page_index = new_page_index + i; + uint32_t map_index = page_index / pages_per_map; + + if (map_index == _page_maps.size()) { + _page_maps.push_back(0); + _page_metadata_maps.push_back(0); + } else if (_page_maps[map_index] == 0) { + make_pages_reusable(page_index, false); + } + + _page_maps[map_index].set(page_index % pages_per_map); + if (i == 0) { + _page_metadata_maps[map_index].set(page_index % pages_per_map); + } + } + + _used_pages_num += needed_pages; + + uint32_t new_region_size = (new_page_index + needed_pages) * page_size; + while (region_capacity() < new_region_size) { + grow_region(); + } + + // ptr offsets are "one"-based, so that we can treat 0 as null. + ptr new_page = ptr((new_page_index + 1) * page_size); + new_page->zone = zone; + new_page->previous = nullptr; + new_page->total = (needed_size + page_mask) & page_alignment; + new_page->in_use = sizeof(page); + unlock(); + return new_page; +} + +// TO BE AUDITED +void table::dealloc_page_locked(ptr page) OG_NOEXCEPT { + int32_t total_bytes = page.get(_data_base)->total; + int32_t num_pages = total_bytes / page_size; + _used_pages_num -= num_pages; + if (total_bytes < page_size) { + return; + } + // convert the page address (starts at 512) to an index (starts at 0) + int32_t page_index = (page / page_size) - 1; + for (int32_t i = 0; i != num_pages; i += 1) { + + int32_t next_page_index = page_index + i; + int32_t next_map_index = next_page_index / pages_per_map; + + _page_maps[next_map_index].reset(next_page_index % pages_per_map); + if (i == 0) { + _page_metadata_maps[next_map_index].reset(next_page_index % pages_per_map); + } + + if (_page_maps[next_map_index].none()) { + make_pages_reusable(next_page_index, true); + } + } +} + +// TO BE AUDITED +uint64_t table::raw_page_seed(ptr page) OG_NOEXCEPT { + page.assert_valid(_data_capacity); + lock(); + uint32_t page_index = page.page_index(); + uint32_t map_index = page_index / pages_per_map; + uint64_t result = 0; + uint32_t w21 = 0; + uint32_t w22 = 0; + // FIXME + if (map_index < _page_metadata_maps.size() && _page_metadata_maps[map_index].test(page_index % page_size)) { + auto info = page->zone->info(); + // FIXME + w22 = info.to_raw_value() & 0xffffff00; + w21 = info.to_raw_value() & 0x000000ff; + result = uint64_t(1) << 32; + } + unlock(); + return result || uint64_t(w22 || w21); +} + +void table::print() const OG_NOEXCEPT { + lock(); + fprintf(stderr, "data::table %p:\n %.2fKB allocated, %.2fKB used, %.2fKB reusable.\n", + this, + double(_region_capacity - page_size) / 1024.0, + double(this->used_pages_num()) / 1024.0, + double(_reusable_pages_num) / 1024.0); + unlock(); +} + +} /* data */ +} /* OG */ + +// AG::data::table::malloc_zone_deleter diff --git a/Sources/OpenGraph_SPI/Data/table.hpp b/Sources/OpenGraph_SPI/Data/table.hpp new file mode 100644 index 00000000..de51d153 --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/table.hpp @@ -0,0 +1,136 @@ +// +// table.hpp +// OpenGraph_SPI + +#ifndef table_hpp +#define table_hpp + +#include "OGBase.h" +#include "../Vector/vector.hpp" +#if OG_TARGET_OS_DARWIN +#include +#else +typedef uintptr_t vm_size_t; +typedef uintptr_t vm_offset_t; +typedef vm_offset_t vm_address_t; +#endif +#include +#if OG_TARGET_OS_DARWIN +#include +#endif + +namespace OG { +namespace data { +class zone; +class page; +template class ptr; + +class table { +public: + class malloc_zone_deleter { + }; + +public: + static table &ensure_shared(); + + OG_INLINE OG_CONSTEXPR + vm_address_t data_base() const OG_NOEXCEPT { return _data_base; }; + + OG_INLINE OG_CONSTEXPR + vm_address_t region_base() const OG_NOEXCEPT { return _region_base; }; + + OG_INLINE OG_CONSTEXPR + void lock() const OG_NOEXCEPT { + #if OG_TARGET_OS_DARWIN + return os_unfair_lock_lock(&_lock); + #endif + }; + + OG_INLINE OG_CONSTEXPR + void unlock() const OG_NOEXCEPT { + #if OG_TARGET_OS_DARWIN + return os_unfair_lock_unlock(&_lock); + #endif + }; + + OG_INLINE OG_CONSTEXPR + uint32_t region_capacity() const OG_NOEXCEPT { return _region_capacity; }; + + OG_INLINE OG_CONSTEXPR + uint32_t data_capacity() const OG_NOEXCEPT { return _data_capacity; }; + + OG_INLINE OG_CONSTEXPR + uint32_t used_pages_num() const OG_NOEXCEPT { return _used_pages_num; }; + + OG_INLINE OG_CONSTEXPR + uint32_t reusable_pages_num() const OG_NOEXCEPT { return _reusable_pages_num; }; + + OG_INLINE OG_CONSTEXPR + uint32_t map_search_start() const OG_NOEXCEPT { return _map_search_start; }; + + OG_INLINE OG_CONSTEXPR + uint32_t zones_num() const OG_NOEXCEPT { return _zones_num; }; + + OG_INLINE OG_CONSTEXPR + uint32_t make_zone_id() { + _zones_num += 1; + return _zones_num; + } + + table(); + + void grow_region() OG_NOEXCEPT; + + void make_pages_reusable(uint32_t page_index, bool reusable) OG_NOEXCEPT; + + ptr alloc_page(zone *zone, uint32_t size) OG_NOEXCEPT; + + void dealloc_page_locked(ptr page) OG_NOEXCEPT; + + uint64_t raw_page_seed(ptr page) OG_NOEXCEPT; + + void print() const OG_NOEXCEPT; +private: + /// _region_base - page_size + vm_address_t _data_base; + + vm_address_t _region_base; + + #if OG_TARGET_OS_DARWIN + mutable os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT; + #endif + + uint32_t _region_capacity; + + /// _region_capactiy + page_size + uint32_t _data_capacity; + + uint32_t _used_pages_num = 0; + + uint32_t _reusable_pages_num = 0; + + uint32_t _map_search_start = 0; + + uint32_t _zones_num = 0; + + using remapped_region = std::pair; + vector _remapped_regions = {}; + + OG_CONSTEXPR static unsigned int pages_per_map = 64; + + using page_map_type = std::bitset; + vector _page_maps = {}; + vector _page_metadata_maps = {}; +}; /* table */ + +static uint8_t _shared_table_bytes[sizeof(table) / sizeof(uint8_t)] = {}; + +OG_INLINE +static table &shared_table() { + return *reinterpret_cast(&_shared_table_bytes); +} + +} /* data */ +} /* OG */ + +#endif /* table_hpp */ diff --git a/Sources/OpenGraph_SPI/Data/zone.cpp b/Sources/OpenGraph_SPI/Data/zone.cpp new file mode 100644 index 00000000..490f487d --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/zone.cpp @@ -0,0 +1,130 @@ +// +// zone.cpp +// OpenGraph_SPI + +#include "zone.hpp" +#include "table.hpp" +#include "page.hpp" +#include "../Util/assert.hpp" +#if OG_TARGET_OS_DARWIN +#include +#else +#include +#endif + +namespace OG { +namespace data { + +void zone::clear() OG_NOEXCEPT { + shared_table().lock(); + while (last_page()) { + auto page = last_page(); + _last_page = page->previous; + shared_table().dealloc_page_locked(page); + } + shared_table().unlock(); +} + +ptr zone::alloc_slow(uint32_t size, uint32_t alignment_mask) OG_NOEXCEPT { + if (last_page()) { + // check if we can use remaining bytes in this page + ptr next_bytes = last_page() + last_page()->in_use; + if (next_bytes.page_ptr() == _last_page) { + ptr aligned_next_bytes = next_bytes.aligned(); + int32_t remaining_size = _last_page->total - _last_page->in_use + (next_bytes - aligned_next_bytes); + if (remaining_size >= sizeof(bytes_info)) { + bytes_info *remaining_bytes = aligned_next_bytes.get(); + remaining_bytes->next = _free_bytes; + remaining_bytes->size = remaining_size; + _free_bytes = aligned_next_bytes; + } + + // consume this entire page + _last_page->in_use = _last_page->total; + } + } + + ptr new_page; + if (size <= page_size / 2) { + new_page = shared_table().alloc_page(this, page_size); + new_page->previous = _last_page; + _last_page = new_page; + } else { + uint32_t aligned_size = ((sizeof(page) + size) + alignment_mask) & ~alignment_mask; + new_page = shared_table().alloc_page(this, aligned_size); + if (_last_page) { + // It's less likely we will be able to alloc unused bytes from this page, + // so insert it before the last page. + new_page->previous = _last_page->previous; + _last_page->previous = new_page; + } else { + _last_page = new_page; + } + } + + int32_t aligned_used_bytes = (new_page->in_use + alignment_mask) & ~alignment_mask; + + // Sanity check + if (aligned_used_bytes + size > new_page->total) { + precondition_failure("internal error"); + } + + new_page->in_use = aligned_used_bytes + size; + return new_page + aligned_used_bytes; +}; + +void zone::print() const OG_NOEXCEPT { + unsigned long num_pages = 0; + double pages_total_kb = 0.0; + double pages_in_use_kb = 0.0; + if (_last_page) { + int64_t pages_total = 0; + int64_t pages_in_use = 0; + for (auto page = _last_page; page; page = page->previous) { + num_pages++; + pages_total += page->total; + pages_in_use += page->in_use; + } + pages_total_kb = pages_total / 1024.0; + pages_in_use_kb = pages_in_use / 1024.0; + } + + unsigned long num_free_elements = 0; + unsigned long free_bytes = 0; + if (_free_bytes) { + for (auto bytes = _free_bytes; bytes; bytes = bytes->next) { + num_free_elements++; + free_bytes += bytes->size; + } + } + + unsigned long num_persistent_buffers = _malloc_buffers.size(); + size_t malloc_total_size = 0; + for (auto &element : _malloc_buffers) { + #if OG_TARGET_OS_DARWIN + malloc_total_size += malloc_size(element.get()); + #else + malloc_total_size += malloc_usable_size(element.get()); + #endif + } + double malloc_total_size_kb = malloc_total_size / 1024.0; + + fprintf(stderr, "%-16p %6lu %8.2f %8.2f %6lu %6lu %6lu %8.2f\n", + this, // zone ptr + num_pages, // pages + pages_total_kb, // total + pages_in_use_kb, // in-use + num_free_elements, // free + free_bytes, // bytes + num_persistent_buffers, // malloc + malloc_total_size_kb // total + ); +} + +void zone::print_header() OG_NOEXCEPT { + fprintf(stderr, "Zones\n%-16s %6s %8s %8s %6s %6s %6s %8s\n", + "zone ptr", "pages", "total", "in-use", "free", "bytes", "malloc", "total"); +} + +} /* data */ +} /* OG */ diff --git a/Sources/OpenGraph_SPI/Data/zone.hpp b/Sources/OpenGraph_SPI/Data/zone.hpp new file mode 100644 index 00000000..5e5b5ca6 --- /dev/null +++ b/Sources/OpenGraph_SPI/Data/zone.hpp @@ -0,0 +1,70 @@ +// +// zone.hpp +// OpenGraph_SPI + +#ifndef zone_hpp +#define zone_hpp + +#include "OGBase.h" +#include "ptr.hpp" + +namespace OG { +namespace data { + +class zone { +public: + class info { + private: + constexpr static uint32_t zone_id_mask = 0x7fffffff; + uint32_t _value; + constexpr info(uint32_t value) : _value(value){}; + + public: + uint32_t zone_id() { return _value & zone_id_mask; }; + info with_zone_id(uint32_t zone_id) const { return info((_value & ~zone_id_mask) | (zone_id & zone_id_mask)); }; + + uint32_t to_raw_value() { return _value; }; + static info from_raw_value(uint32_t value) { return info(value); }; + }; /* info */ +public: + zone(); + + OG_INLINE OG_CONSTEXPR + ptr last_page() const OG_NOEXCEPT { return _last_page; }; + + OG_INLINE OG_CONSTEXPR + info info() const OG_NOEXCEPT { return _info; }; + + void clear() OG_NOEXCEPT; + + ptr alloc_slow(uint32_t size, uint32_t alignment_mask) OG_NOEXCEPT; + + void *alloc_persistent(size_t size) OG_NOEXCEPT; + + void realloc_bytes(ptr *buffer, uint32_t size, uint32_t new_size, uint32_t alignment_mask) OG_NOEXCEPT; + +// ptr alloc_bytes(uint32_t size, uint32_t alignment_mask); + ptr alloc_bytes_recycle(uint32_t size, uint32_t alignment_mask) OG_NOEXCEPT; + + // Printing + void print() const OG_NOEXCEPT; + + void print_header() OG_NOEXCEPT; + + ~zone(); +private: + typedef struct _bytes_info { + ptr next; + uint32_t size; + } bytes_info; + + vector, 0, uint32_t> _malloc_buffers; + ptr _last_page; + ptr _free_bytes; + class info _info; +}; /* zone */ + +} /* data */ +} /* OG */ + +#endif /* zone_hpp */ diff --git a/Sources/OpenGraph_SPI/Debug/og-debug-server.hpp b/Sources/OpenGraph_SPI/Debug/og-debug-server.hpp index 8b056f8c..f0522912 100644 --- a/Sources/OpenGraph_SPI/Debug/og-debug-server.hpp +++ b/Sources/OpenGraph_SPI/Debug/og-debug-server.hpp @@ -11,7 +11,7 @@ #include "OGBase.h" #if OG_TARGET_OS_DARWIN #include "OGDebugServer.h" -#include "../Util/vector.hpp" +#include "../Vector/vector.hpp" #include #include @@ -66,4 +66,4 @@ typedef struct OGDebugServerStorage { OG_ASSUME_NONNULL_END #endif /* OG_TARGET_OS_DARWIN */ -#endif /* og_debug_server_ hpp */ +#endif /* og_debug_server_hpp */ diff --git a/Sources/OpenGraph_SPI/Debug/og-debug-server.mm b/Sources/OpenGraph_SPI/Debug/og-debug-server.mm index ef61f96e..b6d73275 100644 --- a/Sources/OpenGraph_SPI/Debug/og-debug-server.mm +++ b/Sources/OpenGraph_SPI/Debug/og-debug-server.mm @@ -165,7 +165,8 @@ return; } fcntl(server->sockfd, F_SETFD, O_WRONLY); - server->connections.push_back(std::unique_ptr(new Connection(server, sockfd))); + // FIXME: Link issue about vector + // server->connections.push_back(std::unique_ptr(new Connection(server, sockfd))); } CFURLRef _Nullable OG::DebugServer::copy_url() const { @@ -225,7 +226,8 @@ auto it = connections.begin(); for (; it != connections.end(); it++) { if (it->get() == connection) { - connections.pop_back(); + // FIXME: Link issue about vector + // connections.pop_back(); return; } } diff --git a/Sources/OpenGraph_SPI/Graph/Graph.cpp b/Sources/OpenGraph_SPI/Graph/Graph.cpp index d6217fce..6b1cadcb 100644 --- a/Sources/OpenGraph_SPI/Graph/Graph.cpp +++ b/Sources/OpenGraph_SPI/Graph/Graph.cpp @@ -22,7 +22,7 @@ OG::Graph::Graph() OG_NOEXCEPT { // Tracked via https://github.com/swiftwasm/swift/issues/5565 #if !OG_TARGET_OS_WASI static dispatch_once_t make_keys; - dispatch_once_f(&make_keys, nullptr, [](void *context){ + dispatch_once_f(&make_keys, nullptr, [](void * _Nullable context){ pthread_key_create(&_current_update_key, nullptr); OG::Subgraph::make_current_subgraph_key(); }); diff --git a/Sources/OpenGraph_SPI/Graph/OGSubgraph.cpp b/Sources/OpenGraph_SPI/Graph/OGSubgraph.cpp index 534f5672..c9c1476f 100644 --- a/Sources/OpenGraph_SPI/Graph/OGSubgraph.cpp +++ b/Sources/OpenGraph_SPI/Graph/OGSubgraph.cpp @@ -176,7 +176,7 @@ OGUniqueID OGSubgraphAddObserver(OGSubgraphRef cf_subgraph, static bool should_record_tree; static dispatch_once_t should_record_tree_once; -void init_should_record_tree(void *) { +void init_should_record_tree(void * _Nullable context) { should_record_tree = OG::get_env("OG_TREE") != 0; } #endif diff --git a/Sources/OpenGraph_SPI/Util/realloc_vector.hpp b/Sources/OpenGraph_SPI/Util/realloc_vector.hpp index 57ca0ddc..558067bf 100644 --- a/Sources/OpenGraph_SPI/Util/realloc_vector.hpp +++ b/Sources/OpenGraph_SPI/Util/realloc_vector.hpp @@ -9,7 +9,6 @@ #define realloc_vector_hpp #include "OGBase.h" -#include OG_ASSUME_NONNULL_BEGIN diff --git a/Sources/OpenGraph_SPI/Util/vector.hpp b/Sources/OpenGraph_SPI/Util/vector.hpp deleted file mode 100644 index 78ee7625..00000000 --- a/Sources/OpenGraph_SPI/Util/vector.hpp +++ /dev/null @@ -1,159 +0,0 @@ -// -// vector.hpp -// -// -// Created by Kyle on 2024/1/15. -// - -#ifndef vector_hpp -#define vector_hpp - -#include "OGBase.h" -#include "wrap_iter.hpp" -#include "realloc_vector.hpp" -#include -#include - -namespace OG { -template -class vector final { -private: - typedef std::allocator<_Tp> __default_allocator_type; -public: - typedef vector __self; - typedef _Tp value_type; - typedef __default_allocator_type allocator_type; - typedef std::allocator_traits __alloc_traits; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef typename __alloc_traits::size_type size_type; - typedef typename __alloc_traits::difference_type difference_type; - typedef typename __alloc_traits::pointer pointer; - typedef typename __alloc_traits::const_pointer const_pointer; - typedef OG::details::__wrap_iter iterator; - typedef OG::details::__wrap_iter const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - OG_CONSTEXPR void push_back(const_reference __x); - OG_CONSTEXPR void push_back(value_type&& __x); - OG_CONSTEXPR void pop_back(); - - OG_CONSTEXPR const_iterator begin() const OG_NOEXCEPT; - OG_CONSTEXPR const_iterator end() const OG_NOEXCEPT; -private: - // TODO - static OG_CONSTEXPR size_type stack_buffer_byte_size = 0; - - uint8_t _stack_buffer[stack_buffer_byte_size] = {0}; - pointer _buffer; - size_type _count = 0; - size_type _capacity = 0; -}; - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, size, _SizeType>::push_back(const_reference __x) -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, size, _SizeType>::push_back(value_type&& __x) -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, size, _SizeType>::pop_back() -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE -typename OG::vector<_Tp, size, _SizeType>::const_iterator OG::vector<_Tp, size, _SizeType>::begin() const OG_NOEXCEPT -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE -typename OG::vector<_Tp, size, _SizeType>::const_iterator OG::vector<_Tp, size, _SizeType>::end() const OG_NOEXCEPT -{ - // TODO -} - -// MARK: size = 0 - -template -class vector<_Tp, 0, _SizeType> final { -private: - typedef std::allocator<_Tp> __default_allocator_type; -public: - typedef vector __self; - typedef _Tp value_type; - typedef __default_allocator_type allocator_type; - typedef std::allocator_traits __alloc_traits; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef typename __alloc_traits::size_type size_type; - typedef typename __alloc_traits::difference_type difference_type; - typedef typename __alloc_traits::pointer pointer; - typedef typename __alloc_traits::const_pointer const_pointer; - typedef OG::details::__wrap_iter iterator; - typedef OG::details::__wrap_iter const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - OG_CONSTEXPR void push_back(const_reference __x); - OG_CONSTEXPR void push_back(value_type&& __x); - OG_CONSTEXPR void pop_back(); - - OG_CONSTEXPR const_iterator begin() const OG_NOEXCEPT; - OG_CONSTEXPR const_iterator end() const OG_NOEXCEPT; -private: - pointer _buffer = nullptr; - size_type _count = 0; - size_type _capacity = 0; -}; - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, 0, _SizeType>::push_back(const_reference __x) -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, 0, _SizeType>::push_back(value_type&& __x) -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE void -OG::vector<_Tp, 0, _SizeType>::pop_back() -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE -typename OG::vector<_Tp, 0, _SizeType>::const_iterator OG::vector<_Tp, 0, _SizeType>::begin() const OG_NOEXCEPT -{ - // TODO -} - -template -OG_CONSTEXPR OG_INLINE -typename OG::vector<_Tp, 0, _SizeType>::const_iterator OG::vector<_Tp, 0, _SizeType>::end() const OG_NOEXCEPT -{ - // TODO -} - -} -#endif /* vector_hpp */ diff --git a/Sources/OpenGraph_SPI/Util/wrap_iter.hpp b/Sources/OpenGraph_SPI/Util/wrap_iter.hpp deleted file mode 100644 index 3cbb4332..00000000 --- a/Sources/OpenGraph_SPI/Util/wrap_iter.hpp +++ /dev/null @@ -1,232 +0,0 @@ -// -// wrap_iter.hpp -// -// -// Created by Kyle on 2024/1/17. -// - -#ifndef wrap_iter_hpp -#define wrap_iter_hpp - -#include -#include - -namespace OG { -template -class vector; - -namespace details { - -// LLVM-Project wrap_iter.h -// release/17.0.6 + 6009708b4367171ccdbf4b5905cb6a803753fe18 - -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -template -class __wrap_iter -{ -public: - typedef _Iter iterator_type; - typedef typename std::iterator_traits::value_type value_type; - typedef typename std::iterator_traits::difference_type difference_type; - typedef typename std::iterator_traits::pointer pointer; - typedef typename std::iterator_traits::reference reference; - typedef typename std::iterator_traits::iterator_category iterator_category; -#if _LIBCPP_STD_VER >= 20 - typedef contiguous_iterator_tag iterator_concept; -#endif - -private: - iterator_type __i_; -public: - OG_CONSTEXPR __wrap_iter() OG_NOEXCEPT - : __i_() - { - } - template OG_CONSTEXPR - __wrap_iter(const __wrap_iter<_Up>& __u, - typename std::enable_if::value>::type* = nullptr) OG_NOEXCEPT - : __i_(__u.base()) - { - } - OG_CONSTEXPR reference operator*() const OG_NOEXCEPT - { - return *__i_; - } - OG_CONSTEXPR pointer operator->() const OG_NOEXCEPT - { - return std::__to_address(__i_); - } - OG_CONSTEXPR __wrap_iter& operator++() OG_NOEXCEPT - { - ++__i_; - return *this; - } - OG_CONSTEXPR __wrap_iter operator++(int) OG_NOEXCEPT - {__wrap_iter __tmp(*this); ++(*this); return __tmp;} - - OG_CONSTEXPR __wrap_iter& operator--() OG_NOEXCEPT - { - --__i_; - return *this; - } - OG_CONSTEXPR __wrap_iter operator--(int) OG_NOEXCEPT - {__wrap_iter __tmp(*this); --(*this); return __tmp;} - OG_CONSTEXPR __wrap_iter operator+ (difference_type __n) const OG_NOEXCEPT - {__wrap_iter __w(*this); __w += __n; return __w;} - OG_CONSTEXPR __wrap_iter& operator+=(difference_type __n) OG_NOEXCEPT - { - __i_ += __n; - return *this; - } - OG_CONSTEXPR __wrap_iter operator- (difference_type __n) const OG_NOEXCEPT - {return *this + (-__n);} - OG_CONSTEXPR __wrap_iter& operator-=(difference_type __n) OG_NOEXCEPT - {*this += -__n; return *this;} - OG_CONSTEXPR reference operator[](difference_type __n) const OG_NOEXCEPT - { - return __i_[__n]; - } - - OG_CONSTEXPR iterator_type base() const OG_NOEXCEPT {return __i_;} - -private: - OG_CONSTEXPR - explicit __wrap_iter(iterator_type __x) OG_NOEXCEPT : __i_(__x) - { - } - - template friend class __wrap_iter; - template - friend class OG::vector; -}; - -template -OG_CONSTEXPR -bool operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return __x.base() == __y.base(); -} - -template -OG_CONSTEXPR -bool operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return __x.base() == __y.base(); -} - -template -OG_CONSTEXPR -bool operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return __x.base() < __y.base(); -} - -template -OG_CONSTEXPR -bool operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return __x.base() < __y.base(); -} - -template -OG_CONSTEXPR -bool operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return !(__x == __y); -} - -template -OG_CONSTEXPR -bool operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return !(__x == __y); -} - -template -OG_CONSTEXPR -bool operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return __y < __x; -} - -template -OG_CONSTEXPR -bool operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return __y < __x; -} - -template -OG_CONSTEXPR -bool operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return !(__x < __y); -} - -template -OG_CONSTEXPR -bool operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return !(__x < __y); -} - -template -OG_CONSTEXPR -bool operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) OG_NOEXCEPT -{ - return !(__y < __x); -} - -template -OG_CONSTEXPR -bool operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -{ - return !(__y < __x); -} - -template -OG_CONSTEXPR -#ifndef _LIBCPP_CXX03_LANG -auto operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT - -> decltype(__x.base() - __y.base()) -#else -typename __wrap_iter<_Iter1>::difference_type -operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) OG_NOEXCEPT -#endif // C++03 -{ - return __x.base() - __y.base(); -} - -template -OG_CONSTEXPR -__wrap_iter<_Iter1> operator+(typename __wrap_iter<_Iter1>::difference_type __n, __wrap_iter<_Iter1> __x) OG_NOEXCEPT -{ - __x += __n; - return __x; -} -} /* detail */ -} /* OG */ - -namespace std { -template -struct std::pointer_traits > -{ - typedef OG::details::__wrap_iter<_It> pointer; - typedef typename pointer_traits<_It>::element_type element_type; - typedef typename pointer_traits<_It>::difference_type difference_type; - - OG_CONSTEXPR - static element_type *to_address(pointer __w) OG_NOEXCEPT { - return std::__to_address(__w.base()); - } -}; -} /* std */ -#endif /* wrap_iter_hpp */ diff --git a/Sources/OpenGraph_SPI/Vector/vector.hpp b/Sources/OpenGraph_SPI/Vector/vector.hpp new file mode 100644 index 00000000..161c6968 --- /dev/null +++ b/Sources/OpenGraph_SPI/Vector/vector.hpp @@ -0,0 +1,240 @@ +// +// vector.hpp +// OpenGraph_SPI +// +// Status: Complete +// Modified from https://github.com/jcmosc/Compute/blob/00dfebc2c5852144ac5aada8ebe896b78e5f622a/Sources/ComputeCxx/Vector/Vector.h [MIT License] + +#ifndef vector_hpp +#define vector_hpp + +#include "OGBase.h" +#include +#include +#include + +OG_ASSUME_NONNULL_BEGIN + +namespace OG { + +template + requires std::unsigned_integral +class vector { + private: + T _stack_buffer[_stack_size]; + T *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = _stack_size; + + void reserve_slow(size_type new_cap); + + public: + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = value_type *_Nonnull; + using const_iterator = const value_type *_Nonnull; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + ~vector(); + + // Element access + + reference operator[](size_type pos) { return data()[pos]; }; + const_reference operator[](size_type pos) const { return data()[pos]; }; + + reference front() { return &data()[0]; }; + const_reference front() const { return *&data()[0]; }; + reference back() { return *&data()[_size - 1]; }; + const_reference back() const { return *&data()[_size - 1]; }; + + T *_Nonnull data() { return _buffer != nullptr ? _buffer : _stack_buffer; }; + const T *_Nonnull data() const { return _buffer != nullptr ? _buffer : _stack_buffer; }; + + // Iterators + + iterator begin() { return iterator(&data()[0]); }; + const_iterator cbegin() const { return const_iterator(&data()[0]); }; + iterator end() { return iterator(&data()[_size]); }; + const_iterator cend() const { return const_iterator(&data()[_size]); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + + // Capacity + + bool empty() const { return _size == 0; }; + size_type size() const { return _size; }; + void reserve(size_type new_cap); + size_type capacity() const { return _capacity; }; + void shrink_to_fit(); + + // Modifiers + + void clear(); + + void push_back(const T &value); + void push_back(T &&value); + void pop_back(); + + void resize(size_type count); + void resize(size_type count, const value_type &value); +}; + +static_assert(std::contiguous_iterator::iterator>); +static_assert(std::contiguous_iterator::const_iterator>); + +// MARK: Specialization for empty stack buffer + +template + requires std::unsigned_integral +class vector { + private: + T *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = 0; + + void reserve_slow(size_type new_cap); + + public: + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = value_type *_Nonnull; + using const_iterator = const value_type *_Nonnull; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + ~vector(); + + // Element access + + reference operator[](size_type pos) { return data()[pos]; }; + const_reference operator[](size_type pos) const { return data()[pos]; }; + + reference front() { return &data()[0]; }; + const_reference front() const { return *&data()[0]; }; + reference back() { return *&data()[_size - 1]; }; + const_reference back() const { return *&data()[_size - 1]; }; + + T *_Nonnull data() { return _buffer; }; + const T *_Nonnull data() const { return _buffer; }; + + // Iterators + + iterator begin() { return iterator(&data()[0]); }; + iterator end() { return iterator(&data()[_size]); }; + const_iterator cbegin() const { return const_iterator(&data()[0]); }; + const_iterator cend() const { return const_iterator(&data()[_size]); }; + const_iterator begin() const { return cbegin(); }; + const_iterator end() const { return cend(); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; + + // Capacity + + bool empty() const { return _size == 0; }; + size_type size() const { return _size; }; + void reserve(size_type new_cap); + size_type capacity() const { return _capacity; }; + void shrink_to_fit(); + + // Modifiers + + void clear(); + + void push_back(const T &value); + void push_back(T &&value); + void pop_back(); + + void resize(size_type count); + void resize(size_type count, const value_type &value); +}; + +// MARK: Specialization for unique_ptr + +template + requires std::unsigned_integral +class vector, 0, size_type> { + private: + std::unique_ptr *_Nullable _buffer = nullptr; + size_type _size = 0; + size_type _capacity = 0; + + void reserve_slow(size_type new_cap); + + public: + using value_type = std::unique_ptr; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = value_type *_Nonnull; + using const_iterator = const value_type *_Nonnull; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + ~vector(); + + // Element access + + reference operator[](size_type pos) { return data()[pos]; }; + const_reference operator[](size_type pos) const { return data()[pos]; }; + + reference front() { return &data()[0]; }; + const_reference front() const { return *&data()[0]; }; + reference back() { return *&data()[_size - 1]; }; + const_reference back() const { return *&data()[_size - 1]; }; + + std::unique_ptr *_Nonnull data() { return _buffer; }; + const std::unique_ptr *_Nonnull data() const { return _buffer; }; + + // Iterators + + iterator begin() { return iterator(&data()[0]); }; + iterator end() { return iterator(&data()[_size]); }; + const_iterator cbegin() const { return const_iterator(&data()[0]); }; + const_iterator cend() const { return const_iterator(&data()[_size]); }; + const_iterator begin() const { return cbegin(); }; + const_iterator end() const { return cend(); }; + + reverse_iterator rbegin() { return std::reverse_iterator(end()); }; + reverse_iterator rend() { return std::reverse_iterator(begin()); }; + const_reverse_iterator crbegin() const { return std::reverse_iterator(cend()); }; + const_reverse_iterator crend() const { return std::reverse_iterator(cbegin()); }; + const_reverse_iterator rbegin() const { return crbegin(); }; + const_reverse_iterator rend() const { return crend(); }; + + // Capacity + + bool empty() const { return _size == 0; }; + size_type size() const { return _size; }; + void reserve(size_type new_cap); + size_type capacity() const { return _capacity; }; + void shrink_to_fit(); + + // Modifiers + + void clear(); + + void push_back(const std::unique_ptr &value) = delete; + void push_back(std::unique_ptr &&value); + void pop_back(); + + void resize(size_type count); + void resize(size_type count, const value_type &value); +}; + +} /* OG */ + +OG_ASSUME_NONNULL_END + +#include "vector.tpp" + +#endif /* vector.hpp */ diff --git a/Sources/OpenGraph_SPI/Vector/vector.tpp b/Sources/OpenGraph_SPI/Vector/vector.tpp new file mode 100644 index 00000000..f11be1e3 --- /dev/null +++ b/Sources/OpenGraph_SPI/Vector/vector.tpp @@ -0,0 +1,327 @@ +// +// vector.hpp +// OpenGraph_SPI +// +// Status: Complete +// Modified from https://github.com/jcmosc/Compute/blob/00dfebc2c5852144ac5aada8ebe896b78e5f622a/Sources/ComputeCxx/Vector/Vector.tpp [MIT Lisence] + +#include "vector.hpp" +#include "../Util/assert.hpp" + +#include +#if OG_TARGET_OS_DARWIN +#include +#else +#include +#endif /* OG_TARGET_OS_DARWIN */ +#include +#include + + +namespace OG { + +#pragma mark - Base implementation + +namespace details { + +template + requires std::unsigned_integral +void *realloc_vector(void *buffer, void *stack_buffer, size_type stack_size, size_type *size, + size_type preferred_new_size) { + // copy data from heap buffer buffer into stack buffer if possible + if (preferred_new_size <= stack_size) { + if (buffer) { + memcpy(stack_buffer, buffer, preferred_new_size * element_size_bytes); + free(buffer); + *size = stack_size; + } + return nullptr; + } + + #if OG_TARGET_OS_DARWIN + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size_bytes); + #else + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size_bytes); + #endif + size_type new_size = new_size_bytes / element_size_bytes; + if (new_size == *size) { + // nothing to do + return buffer; + } + + void *new_buffer = realloc(buffer, new_size_bytes); + if (!new_buffer) { + precondition_failure("allocation failure"); + } + + // copy data from stack buffer into heap buffer + if (!buffer) { + memcpy(new_buffer, stack_buffer, (*size) * element_size_bytes); + } + + *size = new_size; + return new_buffer; +} + +} // namespace details + +template + requires std::unsigned_integral +vector::~vector() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + if (_buffer) { + free(_buffer); + } +} + +template + requires std::unsigned_integral +void vector::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = reinterpret_cast(details::realloc_vector(_buffer, _stack_buffer, _stack_size, + &_capacity, effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + +template + requires std::unsigned_integral +void vector::shrink_to_fit() { + if (capacity() > _size) { + _buffer = reinterpret_cast( + details::realloc_vector(_buffer, _stack_buffer, _stack_size, &_capacity, _size)); + } +} + +template + requires std::unsigned_integral +void vector::clear() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + _size = 0; +} + +template + requires std::unsigned_integral +void vector::push_back(const T &value) { + reserve(_size + 1); + new (&data()[_size]) value_type(value); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::push_back(T &&value) { + reserve(_size + 1); + new (&data()[_size]) value_type(std::move(value)); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::pop_back() { + assert(size() > 0); + data()[_size - 1].~T(); + _size -= 1; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (this[i]) value_type(); + } + } + _size = count; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count, const value_type &value) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (this[i]) value_type(value); + } + } + _size = count; +} + +#pragma mark - Specialization for empty stack buffer + +namespace details { + +template + requires std::unsigned_integral +void *realloc_vector(void *buffer, size_type *size, size_type preferred_new_size) { + if (preferred_new_size == 0) { + *size = 0; + free(buffer); + return nullptr; + } + + #if OG_TARGET_OS_DARWIN + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size); + #else + size_t new_size_bytes = preferred_new_size * element_size; + #endif + size_type new_size = (size_type)(new_size_bytes / element_size); + if (new_size == *size) { + // nothing to do + return buffer; + } + + void *new_buffer = realloc(buffer, new_size_bytes); + if (!new_buffer) { + precondition_failure("allocation failure"); + } + *size = new_size; + return new_buffer; +} + +} // namespace details + +template + requires std::unsigned_integral +vector::~vector() { + for (auto i = 0; i < _size; i++) { + _buffer[i].~T(); + } + if (_buffer) { + free(_buffer); + } +} + +template + requires std::unsigned_integral +void vector::reserve_slow(size_type new_cap) { + size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); + _buffer = + reinterpret_cast(details::realloc_vector(_buffer, &_capacity, effective_new_cap)); +} + +template + requires std::unsigned_integral +void vector::reserve(size_type new_cap) { + if (new_cap <= capacity()) { + return; + } + reserve_slow(new_cap); +} + +template +requires std::unsigned_integral +void vector::shrink_to_fit() { + if (capacity() > size()) { + _buffer = + reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); + } +} + +template + requires std::unsigned_integral +void vector::clear() { + for (auto i = 0; i < _size; i++) { + data()[i].~T(); + } + _size = 0; +} + +template + requires std::unsigned_integral +void vector::push_back(const T &value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(value); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::push_back(T &&value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(std::move(value)); + _size += 1; +} + +template + requires std::unsigned_integral +void vector::pop_back() { + assert(size() > 0); + data()[_size - 1].~T(); + _size -= 1; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (this[i]) value_type(); + } + } + _size = count; +} + +template + requires std::unsigned_integral +void vector::resize(size_type count, const value_type &value) { + reserve(count); + if (count < _size) { + for (auto i = count; i < _size; i++) { + data()[i].~T(); + } + } else if (count > _size) { + for (auto i = _size; i < count; i++) { + new (this[i]) value_type(value); + } + } + _size = count; +} + +#pragma mark - Specialization for unique_ptr + +template + requires std::unsigned_integral +vector, 0, size_type>::~vector() { + for (auto i = 0; i < _size; i++) { + _buffer[i].reset(); + } + if (_buffer) { + free(_buffer); + } +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::push_back(std::unique_ptr &&value) { + reserve(_size + 1); + new (&_buffer[_size]) value_type(std::move(value)); + _size += 1; +} + +} // /* OG */ diff --git a/Tests/OpenGraph_SPITests/README.md b/Tests/OpenGraph_SPITests/README.md new file mode 100644 index 00000000..4230328b --- /dev/null +++ b/Tests/OpenGraph_SPITests/README.md @@ -0,0 +1,3 @@ +## OpenGraph_SPITests + +Test C++ API of OpenGraph_SPI diff --git a/Tests/OpenGraph_SPITests/table_test_case.mm b/Tests/OpenGraph_SPITests/table_test_case.mm new file mode 100644 index 00000000..aa6fd11c --- /dev/null +++ b/Tests/OpenGraph_SPITests/table_test_case.mm @@ -0,0 +1,24 @@ +// +// table_test_case.mm +// OpenGraph_SPITests + +#include "OGBase.h" + +#if OG_TARGET_OS_DARWIN +#include +#include "Data/table.hpp" + +using namespace OG; + +@interface TableTestCase : XCTestCase +@end + +@implementation TableTestCase + +- (void)setUp { + [super setUp]; + data::table::ensure_shared(); +} + +@end +#endif