diff --git a/android/src/main/cpp/filament_api.cpp b/android/src/main/cpp/filament_api.cpp index a92495b6..c044df09 100644 --- a/android/src/main/cpp/filament_api.cpp +++ b/android/src/main/cpp/filament_api.cpp @@ -56,15 +56,13 @@ extern "C" { void* filament_viewer_new( jobject surface, - const char* opaqueShaderPath, - const char* fadeShaderPath, JNIEnv* env, jobject assetManager ) { ANativeWindow* layer = ANativeWindow_fromSurface(env, surface); am = AAssetManager_fromJava(env, assetManager); - return new FilamentViewer((void*)layer, opaqueShaderPath, fadeShaderPath, loadResource, freeResource); + return new FilamentViewer((void*)layer, loadResource, freeResource); } void render( diff --git a/android/src/main/cpp/filament_api.h b/android/src/main/cpp/filament_api.h index f209498e..bf9db778 100644 --- a/android/src/main/cpp/filament_api.h +++ b/android/src/main/cpp/filament_api.h @@ -8,8 +8,6 @@ void load_skybox(void* viewer, const char* skyboxPath, const char* iblPath); void* filament_viewer_new( void* layer, - const char* opaqueShaderPath, - const char* fadeShaderPath, void* assetManager ); } \ No newline at end of file diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt index 4cf7a07d..149b9045 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt @@ -20,8 +20,6 @@ interface FilamentInterop : Library { fun filament_viewer_new( layer:Object, - opaqueShaderPath:String, - fadeShaderPath:String, env:JNIEnv, am:AssetManager ) : Pointer; diff --git a/ios/Classes/FilamentMethodCallHandler.h b/ios/Classes/FilamentMethodCallHandler.h index c5bfddb5..fb2f4cdb 100644 --- a/ios/Classes/FilamentMethodCallHandler.h +++ b/ios/Classes/FilamentMethodCallHandler.h @@ -3,21 +3,18 @@ #endif /* FilamentNativeViewFactory_h */ #import -#import "FilamentViewController.h" #include "FilamentViewer.hpp" +using namespace polyvox; + static const id VIEW_TYPE = @"app.polyvox.filament/filament_view"; @interface FilamentMethodCallHandler : FlutterMethodChannel - (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:( FlutterResult _Nonnull)result; - (polyvox::FilamentViewer*) _viewer; - (polyvox::ResourceBuffer)loadResource:(const char* const)path; -- (void)freeResource:(void*)mem size:(size_t)size misc:(void*)misc; +- (void)freeResource:(polyvox::ResourceBuffer)rb; - (void)ready; -- (instancetype)initWithController:(FilamentViewController*)controller - registrar:(NSObject*)registrar - viewId:(int64_t)viewId - layer:(void*)layer; - +- (instancetype)initWithRegistrar:(NSObject*)registrar viewId:(int64_t)viewId viewer:(FilamentViewer*)viewer; @end diff --git a/ios/Classes/FilamentMethodCallHandler.mm b/ios/Classes/FilamentMethodCallHandler.mm index f5e95c21..7294abe7 100644 --- a/ios/Classes/FilamentMethodCallHandler.mm +++ b/ios/Classes/FilamentMethodCallHandler.mm @@ -1,34 +1,24 @@ #import "FilamentMethodCallHandler.h" -#import "FilamentViewController.h" #import "FilamentNativeViewFactory.h" -static const FilamentMethodCallHandler* _handler; +static int _resourceId = 0; -static polyvox::ResourceBuffer loadResourceGlobal(const char* name) { - return [_handler loadResource:name]; -} - -static void* freeResourceGlobal(ResourceBuffer rb) { - [_handler freeResource:rb ]; - return nullptr; -} +using namespace polyvox; @implementation FilamentMethodCallHandler { - FilamentViewController *_controller; FlutterMethodChannel* _channel; - polyvox::FilamentViewer* _viewer; - void* _layer; + FilamentViewer* _viewer; NSObject* _registrar; } -- (instancetype)initWithController:(FilamentViewController*)controller - registrar:(NSObject*)registrar +- (instancetype)initWithRegistrar:(NSObject*)registrar viewId:(int64_t)viewId - layer:(void*)layer + viewer:(FilamentViewer*)viewer + { - _layer = layer; _registrar = registrar; - _controller = controller; + _viewer = viewer; + NSString* channelName = [NSString stringWithFormat:@"%@_%d",VIEW_TYPE,viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName @@ -36,65 +26,45 @@ static void* freeResourceGlobal(ResourceBuffer rb) { [_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { [self handleMethodCall:call result:result]; }]; - _handler = self; - - _viewer = new polyvox::FilamentViewer(_layer, nullptr, nullptr, loadResourceGlobal, freeResourceGlobal); - [_controller setViewer:_viewer]; - [_controller startDisplayLink]; - + [self ready]; return self; } - (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:(FlutterResult _Nonnull )result { + if(!_viewer) { + result([FlutterError errorWithCode:@"UNAVAILABLE" + message:@"View unavailable" + details:nil]); + return; + } if([@"loadSkybox" isEqualToString:call.method]) { - if(!_viewer) - return; - _viewer->loadSkybox([call.arguments[0] UTF8String], [call.arguments[1] UTF8String]); result(@"OK"); } else if([@"loadGlb" isEqualToString:call.method]) { - if(!_viewer) - return; // TODO should throw exception here _viewer->loadGlb([call.arguments UTF8String]); result(@"OK"); } else if([@"loadGltf" isEqualToString:call.method]) { - if(!_viewer) - return; // TODO should throw exception here _viewer->loadGltf([call.arguments[0] UTF8String], [call.arguments[1] UTF8String]); result(@"OK"); } else if([@"setCamera" isEqualToString:call.method]) { - if(!_viewer) - return; // TODO should throw exception here _viewer->setCamera([call.arguments UTF8String]); result(@"OK"); } else if([@"panStart" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], true); result(@"OK"); } else if([@"panUpdate" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]); result(@"OK"); } else if([@"panEnd" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabEnd(); result(@"OK"); } else if([@"rotateStart" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabBegin([call.arguments[0] intValue], [call.arguments[1] intValue], false); result(@"OK"); } else if([@"rotateUpdate" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabUpdate([call.arguments[0] intValue], [call.arguments[1] intValue]); result(@"OK"); } else if([@"rotateEnd" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->grabEnd(); result(@"OK"); } else if([@"releaseSourceAssets" isEqualToString:call.method]) { @@ -104,36 +74,29 @@ static void* freeResourceGlobal(ResourceBuffer rb) { NSArray* frameData = call.arguments[0]; NSNumber* numWeights = call.arguments[1]; NSNumber* frameRate = call.arguments[2]; + NSUInteger numElements = [frameData count]; float* framesArr = (float*)malloc([frameData count] *sizeof(float)); for(int i =0 ; i < [frameData count]; i++) { *(framesArr+i) = [[frameData objectAtIndex:i] floatValue]; } - _viewer->animateWeights((float*)framesArr, [numWeights intValue], [frameData count] / numWeights, [frameRate floatValue]); - result(@"OK"); - } else if([@"createMorpher" isEqualToString:call.method]) { - const char* meshName = [call.arguments[0] UTF8String]; - NSArray* primitiveIndices = call.arguments[1]; - int* primitiveIndicesArr = (int*)malloc([primitiveIndices count] *sizeof(int)); - for(int i =0 ; i < [primitiveIndices count]; i++) { - primitiveIndicesArr[i] = [[primitiveIndices objectAtIndex:i] intValue]; - } - _viewer->createMorpher(meshName, primitiveIndicesArr, [primitiveIndices count]); - free(primitiveIndicesArr); + NSUInteger numFrames = numElements / [ numWeights intValue ]; + _viewer->animateWeights((float*)framesArr, [numWeights intValue], numFrames, [frameRate floatValue]); result(@"OK"); } else if([@"playAnimation" isEqualToString:call.method]) { - _viewer->playAnimation([call.arguments intValue]); + int animationIndex = [call.arguments[0] intValue]; + bool loop = call.arguments[1]; + _viewer->playAnimation(animationIndex, loop); result(@"OK"); } else if([@"getTargetNames" isEqualToString:call.method]) { - polyvox::StringList list = _viewer->getTargetNames([call.arguments UTF8String]); + StringList list = _viewer->getTargetNames([call.arguments UTF8String]); NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list.count]; for(int i = 0; i < list.count; i++) { asArray[i] = [NSString stringWithFormat:@"%s", list.strings[i]]; } result(asArray); } else if([@"applyWeights" isEqualToString:call.method]) { - if(!_viewer) - return; + NSArray* nWeights = call.arguments; int count = [nWeights count]; @@ -144,17 +107,14 @@ static void* freeResourceGlobal(ResourceBuffer rb) { _viewer->applyWeights(weights, count); result(@"OK"); } else if([@"zoom" isEqualToString:call.method]) { - if(!_viewer) - return; _viewer->manipulator->scroll(0.0f, 0.0f, [call.arguments floatValue]); result(@"OK"); } else { - result(FlutterMethodNotImplemented); } } -- (polyvox::ResourceBuffer)loadResource:(const char* const)path { +- (ResourceBuffer)loadResource:(const char* const)path { NSString* p = [NSString stringWithFormat:@"%s", path]; NSString* key = [_registrar lookupKeyForAsset:p]; NSString* nsPath = [[NSBundle mainBundle] pathForResource:key @@ -167,12 +127,13 @@ static void* freeResourceGlobal(ResourceBuffer rb) { NSData* buffer = [NSData dataWithContentsOfFile:nsPath]; void* cpy = malloc([buffer length]); memcpy(cpy, [buffer bytes], [buffer length]); // can we avoid this copy somehow? - polyvox::ResourceBuffer rbuf(cpy, [buffer length]); + _resourceId++; + ResourceBuffer rbuf(cpy, [buffer length], _resourceId); return rbuf; } - (void)freeResource:(ResourceBuffer)rb { - free(rb.data); + free((void*)rb.data); } - (void)ready { diff --git a/ios/Classes/FilamentNativeViewFactory.mm b/ios/Classes/FilamentNativeViewFactory.mm index 5f7776a7..e0320e0e 100644 --- a/ios/Classes/FilamentNativeViewFactory.mm +++ b/ios/Classes/FilamentNativeViewFactory.mm @@ -1,7 +1,19 @@ #import "FilamentNativeViewFactory.h" -#import "FilamentViewController.h" #import "FilamentMethodCallHandler.h" +using namespace polyvox; + +static const FilamentMethodCallHandler* _shandler; + +static ResourceBuffer loadResource(const char* name) { + return [_shandler loadResource:name]; +} + +static void* freeResource(ResourceBuffer rb) { + [_shandler freeResource:rb ]; + return nullptr; +} + @implementation FilamentNativeViewFactory { NSObject* _registrar; } @@ -25,28 +37,24 @@ @end - - @implementation FilamentNativeView { FilamentView* _view; - FilamentViewController* _controller; - polyvox::FilamentViewer* _viewer; + FilamentViewer* _viewer; FilamentMethodCallHandler* _handler; void* _layer; } - - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject*)registrar { if (self = [super init]) { _view = [[FilamentView alloc] init]; - _controller = [[FilamentViewController alloc] initWithRegistrar:registrar view:_view]; - [_controller viewDidLoad]; _layer = (__bridge_retained void*)[_view layer]; - _handler = [[FilamentMethodCallHandler alloc] initWithController:_controller registrar:registrar viewId:viewId layer:_layer]; - [_handler ready]; + _viewer = new FilamentViewer(_layer, loadResource, freeResource); + [_view setViewer:_viewer]; + _handler = [[FilamentMethodCallHandler alloc] initWithRegistrar:registrar viewId:viewId viewer:_viewer ]; + _shandler = _handler; } return self; } diff --git a/ios/Classes/FilamentView.h b/ios/Classes/FilamentView.h index dc92fe01..a7d3509f 100644 --- a/ios/Classes/FilamentView.h +++ b/ios/Classes/FilamentView.h @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FilamentView : UIView - (void)setViewer:(polyvox::FilamentViewer*)viewer; +- (void)startDisplayLink; +- (void)stopDisplayLink; @end NS_ASSUME_NONNULL_END diff --git a/ios/Classes/FilamentView.mm b/ios/Classes/FilamentView.mm index 90ed142f..6650c8ed 100644 --- a/ios/Classes/FilamentView.mm +++ b/ios/Classes/FilamentView.mm @@ -20,22 +20,22 @@ #import using namespace std; +using namespace polyvox; @interface FilamentView () - (void)initCommon; -- (void)setViewer:(polyvox::FilamentViewer*)viewer; +- (void)setViewer:(FilamentViewer*)viewer; @end @implementation FilamentView { - polyvox::FilamentViewer* _viewer; + FilamentViewer* _viewer; + CADisplayLink* _displayLink; } -- (void)setViewer:(polyvox::FilamentViewer*)viewer { +- (void)setViewer:(FilamentViewer*)viewer { _viewer = viewer; - _viewer->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height, self.contentScaleFactor); } - - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self initCommon]; @@ -65,9 +65,12 @@ using namespace std; - (void)layoutSubviews { [super layoutSubviews]; + NSLog(@"layout subview"); + if(_viewer) { _viewer->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height, self.contentScaleFactor); } + [self startDisplayLink]; } - (void)setContentScaleFactor:(CGFloat)contentScaleFactor { @@ -77,4 +80,29 @@ using namespace std; } } +- (void)drawRect:(CGRect)rect { + NSLog(@"Drawing rect"); + [super drawRect:rect]; +} + +- (void)startDisplayLink { + NSLog(@"Starting display link"); + + [self stopDisplayLink]; + + // Call our render method 60 times a second. + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; + _displayLink.preferredFramesPerSecond = 60; + [_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; +} + +- (void)stopDisplayLink { + [_displayLink invalidate]; + _displayLink = nil; +} + +- (void)render { + _viewer->render(); +} + @end diff --git a/ios/Classes/FilamentViewController.h b/ios/Classes/FilamentViewController.h deleted file mode 100644 index 7fada3d6..00000000 --- a/ios/Classes/FilamentViewController.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -#import "FilamentView.h" -#import "Flutter/Flutter.h" -#import "FilamentViewer.hpp" - -@interface FilamentViewController : UIViewController - -@property(weak, nonatomic) IBOutlet FilamentView* modelView; -- (void)setViewer:(polyvox::FilamentViewer*)viewer; -- (void)startDisplayLink; -- (void)stopDisplayLink; - --initWithRegistrar:(NSObject*)registrar view:(FilamentView*)view; - -@end diff --git a/ios/Classes/FilamentViewController.mm b/ios/Classes/FilamentViewController.mm deleted file mode 100644 index be8d43a7..00000000 --- a/ios/Classes/FilamentViewController.mm +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "FilamentViewController.h" -#import "FilamentView.h" -#import "FilamentViewer.hpp" -#import - -@implementation FilamentViewController { - CADisplayLink* _displayLink; - NSObject* _registrar; - polyvox::FilamentViewer* _viewer; - FilamentView* _view; -} - -- (instancetype)initWithRegistrar:(NSObject*)registrar - view:(FilamentView*)view { - if (self = [super init]) { - _registrar = registrar; - _view = view; - } - - return self; -} -- (void)setViewer:(polyvox::FilamentViewer*)viewer { - _viewer = viewer; - [_view setViewer:_viewer]; -} -#pragma mark UIViewController methods - -- (void)viewDidLoad { - [super viewDidLoad]; -} - -- (void)viewWillAppear:(BOOL)animated { - [self startDisplayLink]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [self stopDisplayLink]; -} - -- (void)startDisplayLink { - [self stopDisplayLink]; - - // Call our render method 60 times a second. - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; - _displayLink.preferredFramesPerSecond = 60; - [_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; -} - -- (void)stopDisplayLink { - [_displayLink invalidate]; - _displayLink = nil; -} - -- (void)render { - if(_viewer) { - _viewer->render(); - } -} - -- (void)dealloc { - -} - -@end diff --git a/ios/Classes/PolyvoxFilamentPlugin.m b/ios/Classes/PolyvoxFilamentPlugin.m deleted file mode 100644 index c75cf655..00000000 --- a/ios/Classes/PolyvoxFilamentPlugin.m +++ /dev/null @@ -1,15 +0,0 @@ -#import "PolyvoxFilamentPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "polyvox_filament-Swift.h" -#endif - -@implementation PolyvoxFilamentPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftPolyvoxFilamentPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/ios/Classes/SwiftPolyvoxFilamentPlugin.swift b/ios/Classes/SwiftPolyvoxFilamentPlugin.swift deleted file mode 100644 index 40db7fab..00000000 --- a/ios/Classes/SwiftPolyvoxFilamentPlugin.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Flutter -import UIKit - -public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "app.polyvox.filament", binaryMessenger: registrar.messenger()) - let instance = SwiftPolyvoxFilamentPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } -} diff --git a/ios/include/CMakeLists.txt b/ios/include/CMakeLists.txt deleted file mode 100644 index 966a469a..00000000 --- a/ios/include/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -cmake_minimum_required(VERSION 3.19) -project(camutils) - -set(TARGET camutils) -set(PUBLIC_HDR_DIR include) - -# ================================================================================================== -# Sources and headers -# ================================================================================================== -set(PUBLIC_HDRS - include/camutils/Bookmark.h - include/camutils/compiler.h - include/camutils/Manipulator.h -) - -set(SRCS - src/Bookmark.cpp - src/FreeFlightManipulator.h - src/Manipulator.cpp - src/MapManipulator.h - src/OrbitManipulator.h -) - -# ================================================================================================== -# Include and target definitions -# ================================================================================================== -include_directories(${PUBLIC_HDR_DIR}) - -add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS}) - -target_link_libraries(${TARGET} PUBLIC math) - -target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) - -# ================================================================================================== -# Compiler flags -# ================================================================================================== -if (MSVC) - target_compile_options(${TARGET} PRIVATE $<$:/fp:fast>) -else() - target_compile_options(${TARGET} PRIVATE $<$:-ffast-math>) - target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register) -endif() - -# ================================================================================================== -# Installation -# ================================================================================================== -install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR}) -install(DIRECTORY ${PUBLIC_HDR_DIR}/camutils DESTINATION include) - -# ================================================================================================== -# Tests -# ================================================================================================== -if (NOT ANDROID AND NOT WEBGL AND NOT IOS) - add_executable(test_${TARGET} tests/test_camutils.cpp) - target_link_libraries(test_${TARGET} PRIVATE camutils gtest) -endif() diff --git a/ios/include/backend/DriverEnums.h b/ios/include/backend/DriverEnums.h index abd38cc7..95050f2a 100644 --- a/ios/include/backend/DriverEnums.h +++ b/ios/include/backend/DriverEnums.h @@ -49,7 +49,9 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4; static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8; static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES. -static constexpr size_t MAX_SAMPLER_COUNT = 16; // Matches the Adreno Vulkan driver. +static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES. static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects. static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT, @@ -638,6 +640,10 @@ enum class TextureCubemapFace : uint8_t { NEGATIVE_Z = 5, //!< -z face }; +inline constexpr int operator +(TextureCubemapFace rhs) noexcept { + return int(rhs); +} + //! Face offsets for all faces of a cubemap struct FaceOffsets { using size_type = size_t; @@ -900,6 +906,16 @@ enum ShaderType : uint8_t { }; static constexpr size_t PIPELINE_STAGE_COUNT = 2; +struct ShaderStageFlags { + bool vertex : 1; + bool fragment : 1; + bool hasShaderType(ShaderType type) const { + return (vertex && type == ShaderType::VERTEX) || + (fragment && type == ShaderType::FRAGMENT); + } +}; +static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { .vertex = true, .fragment = true }; + /** * Selects which buffers to clear at the beginning of the render pass, as well as which buffers * can be discarded at the beginning and end of the render pass. @@ -948,10 +964,20 @@ struct RenderPassParams { * subpass. If this is zero, the render pass has only one subpass. The least significant bit * specifies that the first color attachment in the render target is a subpass input. * - * For now only 2 subpasses are supported, so only the lower 4 bits are used, one for each color - * attachment (see MRT::TARGET_COUNT). + * For now only 2 subpasses are supported, so only the lower 8 bits are used, one for each color + * attachment (see MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT). */ - uint32_t subpassMask = 0; + uint16_t subpassMask = 0; + + /** + * This mask makes a promise to the backend about read-only usage of the depth attachment (bit + * 0) and the stencil attachment (bit 1). Some backends need to know if writes are disabled in + * order to allow sampling from the depth attachment. + */ + uint16_t readOnlyDepthStencil = 0; + + static constexpr uint16_t READONLY_DEPTH = 1 << 0; + static constexpr uint16_t READONLY_STENCIL = 1 << 1; }; struct PolygonOffset { @@ -965,7 +991,11 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); using FrameCompletedCallback = void(*)(void* user); enum class Workaround : uint16_t { - SPLIT_EASU + // The EASU pass must split because shader compiler flattens early-exit branch + SPLIT_EASU, + // Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for + // the whole render pass. + ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP }; } // namespace backend @@ -1006,6 +1036,7 @@ utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend: utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RasterState& rs); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RenderPassParams& b); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::Viewport& v); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStageFlags stageFlags); #endif #endif // TNT_FILAMENT_BACKEND_DRIVERENUMS_H diff --git a/ios/include/backend/Handle.h b/ios/include/backend/Handle.h index bbea5a1b..5bbfaeab 100644 --- a/ios/include/backend/Handle.h +++ b/ios/include/backend/Handle.h @@ -18,13 +18,16 @@ #define TNT_FILAMENT_BACKEND_HANDLE_H #include +#if !defined(NDEBUG) #include +#endif #include +#include + #include -namespace filament { -namespace backend { +namespace filament::backend { struct HwBufferObject; struct HwFence; @@ -41,7 +44,9 @@ struct HwTimerQuery; struct HwVertexBuffer; /* - * A type handle to a h/w resource + * A handle to a backend resource. HandleBase is for internal use only. + * HandleBase *must* be a trivial for the purposes of calls, that is, it cannot have user-defined + * copy or move constructors. */ //! \privatesection @@ -53,41 +58,54 @@ public: constexpr HandleBase() noexcept: object(nullid) {} - explicit HandleBase(HandleId id) noexcept : object(id) { - assert_invariant(object != nullid); // usually means an uninitialized handle is used - } - - HandleBase(HandleBase const& rhs) noexcept = default; - HandleBase(HandleBase&& rhs) noexcept : object(rhs.object) { - rhs.object = nullid; - } - - HandleBase& operator=(HandleBase const& rhs) noexcept = default; - HandleBase& operator=(HandleBase&& rhs) noexcept { - std::swap(object, rhs.object); - return *this; - } - + // whether this Handle is initialized explicit operator bool() const noexcept { return object != nullid; } + // clear the handle, this doesn't free associated resources void clear() noexcept { object = nullid; } + // compare handles bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; } bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; } + bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; } + bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; } + bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; } + bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; } // get this handle's handleId HandleId getId() const noexcept { return object; } + // initialize a handle, for internal use only. + explicit HandleBase(HandleId id) noexcept : object(id) { + assert_invariant(object != nullid); // usually means an uninitialized handle is used + } + protected: + HandleBase(HandleBase const& rhs) noexcept = default; + HandleBase& operator=(HandleBase const& rhs) noexcept = default; + +private: HandleId object; }; -template +/** + * Type-safe handle to backend resources + * @tparam T Type of the resource + */ +template struct Handle : public HandleBase { - using HandleBase::HandleBase; + Handle() noexcept = default; + + Handle(Handle const& rhs) noexcept = default; + + Handle& operator=(Handle const& rhs) noexcept = default; + + explicit Handle(HandleId id) noexcept : HandleBase(id) { } + + // type-safe Handle cast template::value> > - Handle(Handle const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions) + Handle(Handle const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor) private: #if !defined(NDEBUG) @@ -112,7 +130,6 @@ using TextureHandle = Handle; using TimerQueryHandle = Handle; using VertexBufferHandle = Handle; -} // namespace backend -} // namespace filament +} // namespace filament::backend #endif // TNT_FILAMENT_BACKEND_HANDLE_H diff --git a/ios/include/backend/TargetBufferInfo.h b/ios/include/backend/TargetBufferInfo.h index c43df551..a2f30d45 100644 --- a/ios/include/backend/TargetBufferInfo.h +++ b/ios/include/backend/TargetBufferInfo.h @@ -22,38 +22,19 @@ #include -namespace filament { -namespace backend { +namespace filament::backend { //! \privatesection -class TargetBufferInfo { -public: - // ctor for 2D textures - TargetBufferInfo(Handle h, uint8_t level = 0) noexcept // NOLINT(google-explicit-constructor) - : handle(h), level(level) { } - // ctor for cubemaps - TargetBufferInfo(Handle h, uint8_t level, TextureCubemapFace face) noexcept - : handle(h), level(level), face(face) { } - // ctor for 3D textures - TargetBufferInfo(Handle h, uint8_t level, uint16_t layer) noexcept - : handle(h), level(level), layer(layer) { } - - explicit TargetBufferInfo(TextureCubemapFace face) noexcept : face(face) {} - - explicit TargetBufferInfo(uint16_t layer) noexcept : layer(layer) {} - +struct TargetBufferInfo { // texture to be used as render target Handle handle; + // level to be used uint8_t level = 0; - union { - // face if texture is a cubemap - TextureCubemapFace face; - // for 3D textures - uint16_t layer = 0; - }; - TargetBufferInfo() noexcept { } + + // for cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping + uint16_t layer = 0; }; class MRT { @@ -96,13 +77,12 @@ public: } // this is here for backward compatibility - MRT(Handle h, uint8_t level, uint16_t layer) noexcept - : mInfos{{ h, level, layer }} { + MRT(Handle handle, uint8_t level, uint16_t layer) noexcept + : mInfos{{ handle, level, layer }} { } }; -} // namespace backend -} // namespace filament +} // namespace filament::backend #if !defined(NDEBUG) utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::TargetBufferInfo& tbi); diff --git a/ios/include/common/CallbackUtils.cpp b/ios/include/common/CallbackUtils.cpp deleted file mode 100644 index 5cde5451..00000000 --- a/ios/include/common/CallbackUtils.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CallbackUtils.h" - -#include "private/backend/VirtualMachineEnv.h" - -void acquireCallbackJni(JNIEnv* env, CallbackJni& callbackUtils) { -#ifdef __ANDROID__ - callbackUtils.handlerClass = env->FindClass("android/os/Handler"); - callbackUtils.handlerClass = (jclass) env->NewGlobalRef(callbackUtils.handlerClass); - callbackUtils.post = env->GetMethodID(callbackUtils.handlerClass, - "post", "(Ljava/lang/Runnable;)Z"); -#endif - - callbackUtils.executorClass = env->FindClass("java/util/concurrent/Executor"); - callbackUtils.executorClass = (jclass) env->NewGlobalRef(callbackUtils.executorClass); - callbackUtils.execute = env->GetMethodID(callbackUtils.executorClass, - "execute", "(Ljava/lang/Runnable;)V"); -} - -void releaseCallbackJni(JNIEnv* env, CallbackJni callbackUtils, jobject handler, jobject callback) { - if (handler && callback) { -#ifdef __ANDROID__ - if (env->IsInstanceOf(handler, callbackUtils.handlerClass)) { - env->CallBooleanMethod(handler, callbackUtils.post, callback); - } -#endif - if (env->IsInstanceOf(handler, callbackUtils.executorClass)) { - env->CallVoidMethod(handler, callbackUtils.execute, callback); - } - } - env->DeleteGlobalRef(handler); - env->DeleteGlobalRef(callback); -#ifdef __ANDROID__ - env->DeleteGlobalRef(callbackUtils.handlerClass); -#endif - env->DeleteGlobalRef(callbackUtils.executorClass); -} - -// ----------------------------------------------------------------------------------------------- - -JniCallback* JniCallback::make(JNIEnv* env, jobject handler, jobject callback) { - return new JniCallback(env, handler, callback); -} - -JniCallback::JniCallback(JNIEnv* env, jobject handler, jobject callback) - : mHandler(env->NewGlobalRef(handler)), - mCallback(env->NewGlobalRef(callback)) { - acquireCallbackJni(env, mCallbackUtils); -} - -JniCallback::~JniCallback() = default; - -void JniCallback::post(void* user, filament::backend::CallbackHandler::Callback callback) { - callback(user); -} - -void JniCallback::postToJavaAndDestroy(JniCallback* callback) { - JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment(); - releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback); - delete callback; -} - -// ----------------------------------------------------------------------------------------------- - -JniBufferCallback* JniBufferCallback::make(filament::Engine*, - JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer) { - return new JniBufferCallback(env, handler, callback, std::move(buffer)); -} - -JniBufferCallback::JniBufferCallback(JNIEnv* env, jobject handler, jobject callback, - AutoBuffer&& buffer) - : JniCallback(env, handler, callback), - mBuffer(std::move(buffer)) { -} - -JniBufferCallback::~JniBufferCallback() = default; - -void JniBufferCallback::postToJavaAndDestroy(void*, size_t, void* user) { - JniBufferCallback* callback = (JniBufferCallback*)user; - JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment(); - callback->mBuffer.attachToJniThread(env); - releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback); - delete callback; -} - -// ----------------------------------------------------------------------------------------------- - -JniImageCallback* JniImageCallback::make(filament::Engine*, - JNIEnv* env, jobject handler, jobject callback, long image) { - return new JniImageCallback(env, handler, callback, image); -} - -JniImageCallback::JniImageCallback(JNIEnv* env, jobject handler, jobject callback, long image) - : JniCallback(env, handler, callback), - mImage(image) { -} - -JniImageCallback::~JniImageCallback() = default; - -void JniImageCallback::postToJavaAndDestroy(void*, void* user) { - JniImageCallback* callback = (JniImageCallback*)user; - JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment(); - releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback); - delete callback; -} diff --git a/ios/include/common/CallbackUtils.h b/ios/include/common/CallbackUtils.h deleted file mode 100644 index 8cd8671b..00000000 --- a/ios/include/common/CallbackUtils.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "common/NioUtils.h" - -#include - -#include - -struct CallbackJni { -#ifdef __ANDROID__ - jclass handlerClass = nullptr; - jmethodID post = nullptr; -#endif - jclass executorClass = nullptr; - jmethodID execute = nullptr; -}; - -void acquireCallbackJni(JNIEnv* env, CallbackJni& callbackUtils); -void releaseCallbackJni(JNIEnv* env, CallbackJni callbackUtils, jobject handler, jobject callback); - -struct JniCallback : private filament::backend::CallbackHandler { - JniCallback(JniCallback const &) = delete; - JniCallback(JniCallback&&) = delete; - JniCallback& operator=(JniCallback const &) = delete; - JniCallback& operator=(JniCallback&&) = delete; - - // create a JniCallback - static JniCallback* make(JNIEnv* env, jobject handler, jobject runnable); - - // execute the callback on the java thread and destroy ourselves - static void postToJavaAndDestroy(JniCallback* callback); - - // CallbackHandler interface. - void post(void* user, Callback callback) override; - - // Get the CallbackHandler interface - filament::backend::CallbackHandler* getHandler() noexcept { return this; } - - jobject getCallbackObject() { return mCallback; } - -protected: - JniCallback(JNIEnv* env, jobject handler, jobject runnable); - explicit JniCallback() = default; // this version does nothing - virtual ~JniCallback(); - jobject mHandler{}; - jobject mCallback{}; - CallbackJni mCallbackUtils{}; -}; - - -struct JniBufferCallback : public JniCallback { - // create a JniBufferCallback - static JniBufferCallback* make(filament::Engine* engine, - JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer); - - // execute the callback on the java thread and destroy ourselves - static void postToJavaAndDestroy(void*, size_t, void* user); - -private: - JniBufferCallback(JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer); - virtual ~JniBufferCallback(); - AutoBuffer mBuffer; -}; - -struct JniImageCallback : public JniCallback { - // create a JniImageCallback - static JniImageCallback* make(filament::Engine* engine, JNIEnv* env, jobject handler, - jobject runnable, long image); - - // execute the callback on the java thread and destroy ourselves - static void postToJavaAndDestroy(void*, void* user); - -private: - JniImageCallback(JNIEnv* env, jobject handler, jobject runnable, long image); - virtual ~JniImageCallback(); - long mImage; -}; diff --git a/ios/include/common/NioUtils.cpp b/ios/include/common/NioUtils.cpp deleted file mode 100644 index 3a849978..00000000 --- a/ios/include/common/NioUtils.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/NioUtils.h" - -#include - -#include - -AutoBuffer::AutoBuffer(JNIEnv *env, jobject buffer, jint size, bool commit) noexcept : - mEnv(env), - mDoCommit(commit) { - - mNioUtils.jniClass = env->FindClass("com/google/android/filament/NioUtils"); - mNioUtils.jniClass = (jclass) env->NewGlobalRef(mNioUtils.jniClass); - - mNioUtils.getBasePointer = env->GetStaticMethodID(mNioUtils.jniClass, - "getBasePointer", "(Ljava/nio/Buffer;JI)J"); - mNioUtils.getBaseArray = env->GetStaticMethodID(mNioUtils.jniClass, - "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); - mNioUtils.getBaseArrayOffset = env->GetStaticMethodID(mNioUtils.jniClass, - "getBaseArrayOffset", "(Ljava/nio/Buffer;I)I"); - mNioUtils.getBufferType = env->GetStaticMethodID(mNioUtils.jniClass, - "getBufferType", "(Ljava/nio/Buffer;)I"); - - mBuffer = env->NewGlobalRef(buffer); - - mType = (BufferType) env->CallStaticIntMethod( - mNioUtils.jniClass, mNioUtils.getBufferType, mBuffer); - - switch (mType) { - case BufferType::BYTE: - mShift = 0; - break; - case BufferType::CHAR: - case BufferType::SHORT: - mShift = 1; - break; - case BufferType::INT: - case BufferType::FLOAT: - mShift = 2; - break; - case BufferType::LONG: - case BufferType::DOUBLE: - mShift = 3; - break; - } - - mSize = (size_t) size << mShift; - - jlong address = (jlong) env->GetDirectBufferAddress(mBuffer); - if (address) { - // Direct buffer case - mData = reinterpret_cast(env->CallStaticLongMethod(mNioUtils.jniClass, - mNioUtils.getBasePointer, mBuffer, address, mShift)); - mUserData = mData; - } else { - // wrapped array case - jarray array = (jarray) env->CallStaticObjectMethod(mNioUtils.jniClass, - mNioUtils.getBaseArray, mBuffer); - - jint offset = env->CallStaticIntMethod(mNioUtils.jniClass, - mNioUtils.getBaseArrayOffset, mBuffer, mShift); - - mBaseArray = (jarray) env->NewGlobalRef(array); - switch (mType) { - case BufferType::BYTE: - mData = env->GetByteArrayElements((jbyteArray)mBaseArray, nullptr); - break; - case BufferType::CHAR: - mData = env->GetCharArrayElements((jcharArray)mBaseArray, nullptr); - break; - case BufferType::SHORT: - mData = env->GetShortArrayElements((jshortArray)mBaseArray, nullptr); - break; - case BufferType::INT: - mData = env->GetIntArrayElements((jintArray)mBaseArray, nullptr); - break; - case BufferType::LONG: - mData = env->GetLongArrayElements((jlongArray)mBaseArray, nullptr); - break; - case BufferType::FLOAT: - mData = env->GetFloatArrayElements((jfloatArray)mBaseArray, nullptr); - break; - case BufferType::DOUBLE: - mData = env->GetDoubleArrayElements((jdoubleArray)mBaseArray, nullptr); - break; - } - mUserData = (void *) ((char *) mData + offset); - } -} - -AutoBuffer::AutoBuffer(AutoBuffer &&rhs) noexcept { - mEnv = rhs.mEnv; - std::swap(mData, rhs.mData); - std::swap(mUserData, rhs.mUserData); - std::swap(mSize, rhs.mSize); - std::swap(mType, rhs.mType); - std::swap(mShift, rhs.mShift); - std::swap(mBuffer, rhs.mBuffer); - std::swap(mBaseArray, rhs.mBaseArray); - std::swap(mNioUtils, rhs.mNioUtils); -} - -AutoBuffer::~AutoBuffer() noexcept { - JNIEnv *env = mEnv; - if (mBaseArray) { - jint mode = mDoCommit ? 0 : JNI_ABORT; - switch (mType) { - case BufferType::BYTE: - env->ReleaseByteArrayElements((jbyteArray)mBaseArray, (jbyte *) mData, mode); - break; - case BufferType::CHAR: - env->ReleaseCharArrayElements((jcharArray)mBaseArray, (jchar *) mData, mode); - break; - case BufferType::SHORT: - env->ReleaseShortArrayElements((jshortArray)mBaseArray, (jshort *) mData, mode); - break; - case BufferType::INT: - env->ReleaseIntArrayElements((jintArray)mBaseArray, (jint *) mData, mode); - break; - case BufferType::LONG: - env->ReleaseLongArrayElements((jlongArray)mBaseArray, (jlong *) mData, mode); - break; - case BufferType::FLOAT: - env->ReleaseFloatArrayElements((jfloatArray)mBaseArray, (jfloat *) mData, mode); - break; - case BufferType::DOUBLE: - env->ReleaseDoubleArrayElements((jdoubleArray)mBaseArray, (jdouble *) mData, mode); - break; - } - env->DeleteGlobalRef(mBaseArray); - } - if (mBuffer) { - env->DeleteGlobalRef(mBuffer); - } - mEnv->DeleteGlobalRef(mNioUtils.jniClass); -} diff --git a/ios/include/common/NioUtils.h b/ios/include/common/NioUtils.h deleted file mode 100644 index 72f4656e..00000000 --- a/ios/include/common/NioUtils.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -class AutoBuffer { -public: - enum class BufferType : uint8_t { - BYTE, - CHAR, - SHORT, - INT, - LONG, - FLOAT, - DOUBLE - }; - - // Clients should pass "true" for the commit argument if they intend to mutate the buffer - // contents from native code. - AutoBuffer(JNIEnv* env, jobject buffer, jint size, bool commit = false) noexcept; - AutoBuffer(AutoBuffer&& rhs) noexcept; - ~AutoBuffer() noexcept; - - void attachToJniThread(JNIEnv* env) noexcept { - mEnv = env; - } - - void* getData() const noexcept { - return mUserData; - } - - size_t getSize() const noexcept { - return mSize; - } - - size_t getShift() const noexcept { - return mShift; - } - - size_t countToByte(size_t count) const noexcept { - return count << mShift; - } - -private: - void* mUserData = nullptr; - size_t mSize = 0; - BufferType mType = BufferType::BYTE; - uint8_t mShift = 0; - - JNIEnv* mEnv; - void* mData = nullptr; - jobject mBuffer = nullptr; - jarray mBaseArray = nullptr; - bool mDoCommit = false; - - struct { - jclass jniClass; - jmethodID getBasePointer; - jmethodID getBaseArray; - jmethodID getBaseArrayOffset; - jmethodID getBufferType; - } mNioUtils{}; -}; diff --git a/ios/include/filamat/Enums.h b/ios/include/filamat/Enums.h new file mode 100644 index 00000000..ea626e81 --- /dev/null +++ b/ios/include/filamat/Enums.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_ENUMMANAGER_H +#define TNT_ENUMMANAGER_H + +#include +#include +#include + +#include + +namespace filamat { + +using Property = MaterialBuilder::Property; +using UniformType = MaterialBuilder::UniformType; +using SamplerType = MaterialBuilder::SamplerType; +using SubpassType = MaterialBuilder::SubpassType; +using SamplerFormat = MaterialBuilder::SamplerFormat; +using ParameterPrecision = MaterialBuilder::ParameterPrecision; +using OutputTarget = MaterialBuilder::OutputTarget; +using OutputQualifier = MaterialBuilder::VariableQualifier; +using OutputType = MaterialBuilder::OutputType; + +// Convenience methods to convert std::string to Enum and also iterate over Enum values. +class Enums { +public: + + // Returns true if string "s" is a valid string representation of an element of enum T. + template + static bool isValid(const std::string& s) noexcept { + std::unordered_map& map = getMap(); + return map.find(s) != map.end(); + } + + // Return enum matching its string representation. Returns undefined if s is not a valid enum T + // value. You should always call isValid() first to validate a string before calling toEnum(). + template + static T toEnum(const std::string& s) noexcept { + std::unordered_map& map = getMap(); + return map.at(s); + } + + template + static std::string toString(T t) noexcept; + + // Return a map of all values in an enum with their string representation. + template + static std::unordered_map& map() noexcept { + std::unordered_map& map = getMap(); + return map; + }; + +private: + template + static std::unordered_map& getMap() noexcept; + + static std::unordered_map mStringToProperty; + static std::unordered_map mStringToUniformType; + static std::unordered_map mStringToSamplerType; + static std::unordered_map mStringToSubpassType; + static std::unordered_map mStringToSamplerFormat; + static std::unordered_map mStringToSamplerPrecision; + static std::unordered_map mStringToOutputTarget; + static std::unordered_map mStringToOutputQualifier; + static std::unordered_map mStringToOutputType; +}; + +template +std::string Enums::toString(T t) noexcept { + std::unordered_map& map = getMap(); + auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) { + return pair.second == t; + }); + if (result != map.end()) { + return result->first; + } + return ""; +} + +} // namespace filamat + +#endif //TNT_ENUMMANAGER_H diff --git a/ios/include/filamat/IncludeCallback.h b/ios/include/filamat/IncludeCallback.h new file mode 100644 index 00000000..659ba289 --- /dev/null +++ b/ios/include/filamat/IncludeCallback.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMAT_INCLUDER_H +#define TNT_FILAMAT_INCLUDER_H + +#include + +#include + +namespace filamat { + +struct IncludeResult { + // The include name of the root file, as if it were being included. + // I.e., 'foobar.h' in the case of #include "foobar.h" + const utils::CString includeName; + + // The following fields should be filled out by the IncludeCallback when processing an include, + // or when calling resolveIncludes for the root file. + + // The full contents of the include file. This may contain additional, recursive include + // directives. + utils::CString text; + + // The line number for the first line of text (first line is 0). + size_t lineNumberOffset = 0; + + // The name of the include file. This gets passed as "includerName" for any includes inside of + // source. This field isn't used by the include system; it's up to the callback to give meaning + // to this value and interpret it accordingly. In the case of DirIncluder, this is an empty + // string to represent the root include file, and a canonical path for subsequent included + // files. + utils::CString name; +}; + +/** + * A callback invoked by the include system when an #include "file.h" directive is found. + * + * For example, if a file main.h includes file.h on line 10, then IncludeCallback would be called + * with the following: + * includeCallback("main.h", {.includeName = "file.h" }) + * It's then up to the IncludeCallback to fill out the .text, .name, and (optionally) + * lineNumberOffset fields. + * + * @param includedBy is the value that was given to IncludeResult.name for this source file, or + * the empty string for the root source file. + * @param result is the IncludeResult that the callback should fill out. + * @return true, if the include was resolved successfully, false otherwise. + * + * For an example of implementing this callback, see tools/matc/src/matc/DirIncluder.h. + */ +using IncludeCallback = std::function; + +} // namespace filamat + +#endif diff --git a/ios/include/filamat/MaterialBuilder.h b/ios/include/filamat/MaterialBuilder.h new file mode 100644 index 00000000..435aed0b --- /dev/null +++ b/ios/include/filamat/MaterialBuilder.h @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H +#define TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace utils { +class JobSystem; +} + +namespace filamat { + +struct MaterialInfo; +struct Variant; +class ChunkContainer; + +class UTILS_PUBLIC MaterialBuilderBase { +public: + /** + * High-level hint that works in concert with TargetApi to determine the shader models (used to + * generate GLSL) and final output representations (spirv and/or text). + */ + enum class Platform { + DESKTOP, + MOBILE, + ALL + }; + + enum class TargetApi : uint8_t { + OPENGL = 0x01u, + VULKAN = 0x02u, + METAL = 0x04u, + ALL = OPENGL | VULKAN | METAL + }; + + enum class TargetLanguage { + GLSL, // GLSL with OpenGL semantics + SPIRV // GLSL with Vulkan semantics + }; + + enum class Optimization { + NONE, + PREPROCESSOR, + SIZE, + PERFORMANCE + }; + + /** + * Initialize MaterialBuilder. + * + * init must be called first before building any materials. + */ + static void init(); + + /** + * Release internal MaterialBuilder resources. + * + * Call shutdown when finished building materials to release all internal resources. After + * calling shutdown, another call to MaterialBuilder::init must precede another material build. + */ + static void shutdown(); + +protected: + // Looks at platform and target API, then decides on shader models and output formats. + void prepare(bool vulkanSemantics); + + using ShaderModel = filament::backend::ShaderModel; + Platform mPlatform = Platform::DESKTOP; + TargetApi mTargetApi = (TargetApi) 0; + Optimization mOptimization = Optimization::PERFORMANCE; + bool mPrintShaders = false; + bool mGenerateDebugInfo = false; + utils::bitset32 mShaderModels; + struct CodeGenParams { + int shaderModel; + TargetApi targetApi; + TargetLanguage targetLanguage; + }; + std::vector mCodeGenPermutations; + // For finding properties and running semantic analysis, we always use the same code gen + // permutation. This is the first permutation generated with default arguments passed to matc. + const CodeGenParams mSemanticCodeGenParams = { + .shaderModel = (int) ShaderModel::GL_ES_30, + .targetApi = TargetApi::OPENGL, + .targetLanguage = TargetLanguage::SPIRV + }; + + // Keeps track of how many times MaterialBuilder::init() has been called without a call to + // MaterialBuilder::shutdown(). Internally, glslang does something similar. We keep track for + // ourselves so we can inform the user if MaterialBuilder::init() hasn't been called before + // attempting to build a material. + static std::atomic materialBuilderClients; +}; + +// Utility function that looks at an Engine backend to determine TargetApi +inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend( + filament::backend::Backend backend) noexcept { + using filament::backend::Backend; + using TargetApi = MaterialBuilderBase::TargetApi; + switch (backend) { + case Backend::DEFAULT: return TargetApi::ALL; + case Backend::OPENGL: return TargetApi::OPENGL; + case Backend::VULKAN: return TargetApi::VULKAN; + case Backend::METAL: return TargetApi::METAL; + case Backend::NOOP: return TargetApi::OPENGL; + } +} + +/** + * MaterialBuilder builds Filament materials from shader code. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filamat; + * + * // Must be called before any materials can be built. + * MaterialBuilder::init(); + + * MaterialBuilder builder; + * builder + * .name("My material") + * .material("void material (inout MaterialInputs material) {" + * " prepareMaterial(material);" + * " material.baseColor.rgb = float3(1.0, 0.0, 0.0);" + * "}") + * .shading(MaterialBuilder::Shading::LIT) + * .targetApi(MaterialBuilder::TargetApi::ALL) + * .platform(MaterialBuilder::Platform::ALL); + + * Package package = builder.build(); + * if (package.isValid()) { + * // success! + * } + + * // Call when finished building all materials to release internal + * // MaterialBuilder resources. + * MaterialBuilder::shutdown(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see filament::Material + */ +class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { +public: + MaterialBuilder(); + + static constexpr size_t MATERIAL_VARIABLES_COUNT = 4; + enum class Variable : uint8_t { + CUSTOM0, + CUSTOM1, + CUSTOM2, + CUSTOM3 + // when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT + }; + + using MaterialDomain = filament::MaterialDomain; + using RefractionMode = filament::RefractionMode; + using RefractionType = filament::RefractionType; + using ReflectionMode = filament::ReflectionMode; + using VertexAttribute = filament::VertexAttribute; + + using ShaderQuality = filament::ShaderQuality; + using BlendingMode = filament::BlendingMode; + using Shading = filament::Shading; + using Interpolation = filament::Interpolation; + using VertexDomain = filament::VertexDomain; + using TransparencyMode = filament::TransparencyMode; + using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion; + + using UniformType = filament::backend::UniformType; + using SamplerType = filament::backend::SamplerType; + using SubpassType = filament::backend::SubpassType; + using SamplerFormat = filament::backend::SamplerFormat; + using ParameterPrecision = filament::backend::Precision; + using CullingMode = filament::backend::CullingMode; + + enum class VariableQualifier : uint8_t { + OUT + }; + + enum class OutputTarget : uint8_t { + COLOR, + DEPTH + }; + + enum class OutputType : uint8_t { + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4 + }; + + struct PreprocessorDefine { + std::string name; + std::string value; + + PreprocessorDefine(const std::string& name, const std::string& value) : + name(name), value(value) {} + }; + using PreprocessorDefineList = std::vector; + + //! Set the name of this material. + MaterialBuilder& name(const char* name) noexcept; + + //! Set the file name of this material file. Used in error reporting. + MaterialBuilder& fileName(const char* name) noexcept; + + //! Set the shading model. + MaterialBuilder& shading(Shading shading) noexcept; + + //! Set the interpolation mode. + MaterialBuilder& interpolation(Interpolation interpolation) noexcept; + + //! Add a parameter (i.e., a uniform) to this material. + MaterialBuilder& parameter(UniformType type, ParameterPrecision precision, + const char* name) noexcept; + + //! Add a parameter (i.e., a uniform) to this material. + MaterialBuilder& parameter(UniformType type, const char* name) noexcept { + return parameter(type, ParameterPrecision::DEFAULT, name); + } + + //! Add a parameter array to this material. + MaterialBuilder& parameter(UniformType type, size_t size, + ParameterPrecision precision, const char* name) noexcept; + + //! Add a parameter array to this material. + MaterialBuilder& parameter(UniformType type, size_t size, const char* name) noexcept { + return parameter(type, size, ParameterPrecision::DEFAULT, name); + } + + /** + * Add a sampler parameter to this material. + * + * When SamplerType::SAMPLER_EXTERNAL is specifed, format and precision are ignored. + */ + MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format, + ParameterPrecision precision, const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format, + const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, ParameterPrecision precision, + const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, const char* name) noexcept; + + //! Custom variables (all float4). + MaterialBuilder& variable(Variable v, const char* name) noexcept; + + /** + * Require a specified attribute. + * + * position is always required and normal depends on the shading model. + */ + MaterialBuilder& require(VertexAttribute attribute) noexcept; + + //! Specify the domain that this material will operate in. + MaterialBuilder& materialDomain(filament::MaterialDomain materialDomain) noexcept; + + /** + * Set the code content of this material. + * + * Surface Domain + * -------------- + * + * Materials in the SURFACE domain must declare a function: + * ~~~~~ + * void material(inout MaterialInputs material) { + * prepareMaterial(material); + * material.baseColor.rgb = float3(1.0, 0.0, 0.0); + * } + * ~~~~~ + * this function *must* call `prepareMaterial(material)` before it returns. + * + * Post-process Domain + * ------------------- + * + * Materials in the POST_PROCESS domain must declare a function: + * ~~~~~ + * void postProcess(inout PostProcessInputs postProcess) { + * postProcess.color = float4(1.0); + * } + * ~~~~~ + * + * @param code The source code of the material. + * @param line The line number offset of the material, where 0 is the first line. Used for error + * reporting + */ + MaterialBuilder& material(const char* code, size_t line = 0) noexcept; + + /** + * Set the callback used for resolving include directives. + * The default is no callback, which disallows all includes. + */ + MaterialBuilder& includeCallback(IncludeCallback callback) noexcept; + + /** + * Set the vertex code content of this material. + * + * Surface Domain + * -------------- + * + * Materials in the SURFACE domain must declare a function: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * void materialVertex(inout MaterialVertexInputs material) { + * + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Post-process Domain + * ------------------- + * + * Materials in the POST_PROCESS domain must declare a function: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * void postProcessVertex(inout PostProcessVertexInputs postProcess) { + * + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * @param code The source code of the material. + * @param line The line number offset of the material, where 0 is the first line. Used for error + * reporting + */ + MaterialBuilder& materialVertex(const char* code, size_t line = 0) noexcept; + + + MaterialBuilder& quality(ShaderQuality quality) noexcept; + + //! Set the blending mode for this material. + MaterialBuilder& blending(BlendingMode blending) noexcept; + + /** + * Set the blending mode of the post-lighting color for this material. + * Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT. + * This setting requires the material property "postLightingColor" to be set. + */ + MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept; + + //! Set the vertex domain for this material. + MaterialBuilder& vertexDomain(VertexDomain domain) noexcept; + + /** + * How triangles are culled by default (doesn't affect points or lines, BACK by default). + * Material instances can override this. + */ + MaterialBuilder& culling(CullingMode culling) noexcept; + + //! Enable / disable color-buffer write (enabled by default, material instances can override). + MaterialBuilder& colorWrite(bool enable) noexcept; + + //! Enable / disable depth-buffer write (enabled by default for opaque, disabled for others, material instances can override). + MaterialBuilder& depthWrite(bool enable) noexcept; + + //! Enable / disable depth based culling (enabled by default, material instances can override). + MaterialBuilder& depthCulling(bool enable) noexcept; + + /** + * Double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE). + * doubleSided() overrides culling() if called. + * When called with "false", this enables the capability for a run-time toggle. + */ + MaterialBuilder& doubleSided(bool doubleSided) noexcept; + + /** + * Any fragment with an alpha below this threshold is clipped (MASKED blending mode only). + * The mask threshold can also be controlled by using the float material parameter called + * `_maskThreshold`, or by calling + * @ref filament::MaterialInstance::setMaskThreshold "MaterialInstance::setMaskThreshold". + */ + MaterialBuilder& maskThreshold(float threshold) noexcept; + + //! The material output is multiplied by the shadowing factor (UNLIT model only). + MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept; + + //! This material casts transparent shadows. The blending mode must be TRANSPARENT or FADE. + MaterialBuilder& transparentShadow(bool transparentShadow) noexcept; + + /** + * Reduces specular aliasing for materials that have low roughness. Turning this feature on also + * helps preserve the shapes of specular highlights as an object moves away from the camera. + * When turned on, two float material parameters are added to control the effect: + * `_specularAAScreenSpaceVariance` and `_specularAAThreshold`. You can also use + * @ref filament::MaterialInstance::setSpecularAntiAliasingVariance + * "MaterialInstance::setSpecularAntiAliasingVariance" and + * @ref filament::MaterialInstance::setSpecularAntiAliasingThreshold + * "setSpecularAntiAliasingThreshold" + * + * Disabled by default. + */ + MaterialBuilder& specularAntiAliasing(bool specularAntiAliasing) noexcept; + + /** + * Sets the screen-space variance of the filter kernel used when applying specular + * anti-aliasing. The default value is set to 0.15. The specified value should be between 0 and + * 1 and will be clamped if necessary. + */ + MaterialBuilder& specularAntiAliasingVariance(float screenSpaceVariance) noexcept; + + /** + * Sets the clamping threshold used to suppress estimation errors when applying specular + * anti-aliasing. The default value is set to 0.2. The specified value should be between 0 and 1 + * and will be clamped if necessary. + */ + MaterialBuilder& specularAntiAliasingThreshold(float threshold) noexcept; + + /** + * Enables or disables the index of refraction (IoR) change caused by the clear coat layer when + * present. When the IoR changes, the base color is darkened. Disabling this feature preserves + * the base color as initially specified. + * + * Enabled by default. + */ + MaterialBuilder& clearCoatIorChange(bool clearCoatIorChange) noexcept; + + //! Enable / disable flipping of the Y coordinate of UV attributes, enabled by default. + MaterialBuilder& flipUV(bool flipUV) noexcept; + + //! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile. + MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept; + + //! Set the specular ambient occlusion technique. Disabled by default on mobile. + MaterialBuilder& specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept; + + //! Specify the refraction + MaterialBuilder& refractionMode(RefractionMode refraction) noexcept; + + //! Specify the refraction type + MaterialBuilder& refractionType(RefractionType refractionType) noexcept; + + //! Specifies how reflections should be rendered (default is DEFAULT). + MaterialBuilder& reflectionMode(ReflectionMode mode) noexcept; + + //! Specifies how transparent objects should be rendered (default is DEFAULT). + MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept; + + /** + * Enable / disable custom surface shading. Custom surface shading requires the LIT + * shading model. In addition, the following function must be defined in the fragment + * block: + * + * ~~~~~ + * vec3 surfaceShading(const MaterialInputs materialInputs, + * const ShadingData shadingData, const LightData lightData) { + * + * return vec3(1.0); // Compute surface shading with custom BRDF, etc. + * } + * ~~~~~ + * + * This function is invoked once per light. Please refer to the materials documentation + * for more information about the different parameters. + * + * @param customSurfaceShading Enables or disables custom surface shading + */ + MaterialBuilder& customSurfaceShading(bool customSurfaceShading) noexcept; + + /** + * Specifies desktop vs mobile; works in concert with TargetApi to determine the shader models + * (used to generate code) and final output representations (spirv and/or text). + */ + MaterialBuilder& platform(Platform platform) noexcept; + + /** + * Specifies OpenGL, Vulkan, or Metal. + * This can be called repeatedly to build for multiple APIs. + * Works in concert with Platform to determine the shader models (used to generate code) and + * final output representations (spirv and/or text). + * If linking against filamat_lite, only `OPENGL` is allowed. + */ + MaterialBuilder& targetApi(TargetApi targetApi) noexcept; + + /** + * Specifies the level of optimization to apply to the shaders (default is PERFORMANCE). + * If linking against filamat_lite, this _must_ be called with Optimization::NONE. + */ + MaterialBuilder& optimization(Optimization optimization) noexcept; + + // TODO: this is present here for matc's "--print" flag, but ideally does not belong inside + // MaterialBuilder. + //! If true, will output the generated GLSL shader code to stdout. + MaterialBuilder& printShaders(bool printShaders) noexcept; + + //! If true, will include debugging information in generated SPIRV. + MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept; + + //! Specifies a list of variants that should be filtered out during code generation. + MaterialBuilder& variantFilter(filament::UserVariantFilterMask variantFilter) noexcept; + + //! Adds a new preprocessor macro definition to the shader code. Can be called repeatedly. + MaterialBuilder& shaderDefine(const char* name, const char* value) noexcept; + + //! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain. + MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, + OutputType type, const char* name, int location = -1) noexcept; + + MaterialBuilder& enableFramebufferFetch() noexcept; + + /** + * Legacy morphing uses the data in the VertexAttribute slots (\c MORPH_POSITION_0, etc) and is + * limited to 4 morph targets. See filament::RenderableManager::Builder::morphing(). + */ + MaterialBuilder& useLegacyMorphing() noexcept; + + /** + * Build the material. If you are using the Filament engine with this library, you should use + * the job system provided by Engine. + */ + Package build(utils::JobSystem& jobSystem) noexcept; + +public: + // The methods and types below are for internal use + /// @cond never + + /** + * Add a subpass parameter to this material. + */ + MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, ParameterPrecision + precision, const char* name) noexcept; + MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name) + noexcept; + MaterialBuilder& parameter(SubpassType subpassType, ParameterPrecision precision, + const char* name) noexcept; + MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept; + + struct Parameter { + Parameter() noexcept : parameterType(INVALID) {} + + // Sampler + Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p) + : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { } + + // Uniform + Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p) + : name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { } + + // Subpass + Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p) + : name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { } + + utils::CString name; + size_t size; + UniformType uniformType; + ParameterPrecision precision; + SamplerType samplerType; + SubpassType subpassType; + SamplerFormat format; + enum { + INVALID, + UNIFORM, + SAMPLER, + SUBPASS + } parameterType; + + bool isSampler() const { return parameterType == SAMPLER; } + bool isUniform() const { return parameterType == UNIFORM; } + bool isSubpass() const { return parameterType == SUBPASS; } + }; + + struct Output { + Output() noexcept = default; + Output(const char* outputName, VariableQualifier qualifier, OutputTarget target, + OutputType type, int location) noexcept + : name(outputName), qualifier(qualifier), target(target), type(type), + location(location) { } + + utils::CString name; + VariableQualifier qualifier; + OutputTarget target; + OutputType type; + int location; + }; + + static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT; + using Property = filament::Property; + + using PropertyList = bool[MATERIAL_PROPERTIES_COUNT]; + using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT]; + using OutputList = std::vector; + + static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; + static constexpr size_t MAX_DEPTH_OUTPUT = 1; + static_assert(MAX_COLOR_OUTPUT == 8, + "When updating MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT, manually update post_process_inputs.fs" + " and post_process.fs"); + + // Preview the first shader generated by the given CodeGenParams. + // This is used to run Static Code Analysis before generating a package. + std::string peek(filament::backend::ShaderType type, + const CodeGenParams& params, const PropertyList& properties) noexcept; + + // Returns true if any of the parameter samplers is of type samplerExternal + bool hasExternalSampler() const noexcept; + + static constexpr size_t MAX_PARAMETERS_COUNT = 48; + static constexpr size_t MAX_SUBPASS_COUNT = 1; + using ParameterList = Parameter[MAX_PARAMETERS_COUNT]; + + // returns the number of parameters declared in this material + uint8_t getParameterCount() const noexcept { return mParameterCount; } + + // returns a list of at least getParameterCount() parameters + const ParameterList& getParameters() const noexcept { return mParameters; } + + filament::UserVariantFilterMask getVariantFilter() const { return mVariantFilter; } + + /// @endcond + +private: + void prepareToBuild(MaterialInfo& info) noexcept; + + // Return true if the shader is syntactically and semantically valid. + // This method finds all the properties defined in the fragment and + // vertex shaders of the material. + bool findAllProperties() noexcept; + // Multiple calls to findProperties accumulate the property sets across fragment + // and vertex shaders in mProperties. + bool findProperties(filament::backend::ShaderType type, + MaterialBuilder::PropertyList& p) noexcept; + bool runSemanticAnalysis() noexcept; + + bool checkLiteRequirements() noexcept; + + void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept; + void writeSurfaceChunks(ChunkContainer& container) const noexcept; + + bool generateShaders( + utils::JobSystem& jobSystem, + const std::vector& variants, ChunkContainer& container, + const MaterialInfo& info) const noexcept; + + bool hasCustomVaryings() const noexcept; + bool needsStandardDepthProgram() const noexcept; + + bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; } + + utils::CString mMaterialName; + utils::CString mFileName; + + class ShaderCode { + public: + void setLineOffset(size_t offset) noexcept { mLineOffset = offset; } + void setUnresolved(const utils::CString& code) noexcept { + mIncludesResolved = false; + mCode = code; + } + + // Resolve all the #include directives, returns true if successful. + bool resolveIncludes(IncludeCallback callback, const utils::CString& fileName) noexcept; + + const utils::CString& getResolved() const noexcept { + assert(mIncludesResolved); + return mCode; + } + + size_t getLineOffset() const noexcept { return mLineOffset; } + + private: + utils::CString mCode; + size_t mLineOffset = 0; + bool mIncludesResolved = false; + }; + + ShaderCode mMaterialFragmentCode; + ShaderCode mMaterialVertexCode; + + IncludeCallback mIncludeCallback = nullptr; + + PropertyList mProperties; + ParameterList mParameters; + VariableList mVariables; + OutputList mOutputs; + + ShaderQuality mShaderQuality = ShaderQuality::DEFAULT; + BlendingMode mBlendingMode = BlendingMode::OPAQUE; + BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT; + CullingMode mCullingMode = CullingMode::BACK; + Shading mShading = Shading::LIT; + MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; + RefractionMode mRefractionMode = RefractionMode::NONE; + RefractionType mRefractionType = RefractionType::SOLID; + ReflectionMode mReflectionMode = ReflectionMode::DEFAULT; + Interpolation mInterpolation = Interpolation::SMOOTH; + VertexDomain mVertexDomain = VertexDomain::OBJECT; + TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; + + filament::AttributeBitset mRequiredAttributes; + + float mMaskThreshold = 0.4f; + float mSpecularAntiAliasingVariance = 0.15f; + float mSpecularAntiAliasingThreshold = 0.2f; + + bool mShadowMultiplier = false; + bool mTransparentShadow = false; + + uint8_t mParameterCount = 0; + + bool mDoubleSided = false; + bool mDoubleSidedCapability = false; + bool mColorWrite = true; + bool mDepthTest = true; + bool mDepthWrite = true; + bool mDepthWriteSet = false; + + bool mSpecularAntiAliasing = false; + bool mClearCoatIorChange = true; + + bool mFlipUV = true; + + bool mMultiBounceAO = false; + bool mMultiBounceAOSet = false; + + SpecularAmbientOcclusion mSpecularAO = SpecularAmbientOcclusion::NONE; + bool mSpecularAOSet = false; + + bool mCustomSurfaceShading = false; + + bool mEnableFramebufferFetch = false; + + bool mUseLegacyMorphing = false; + + PreprocessorDefineList mDefines; + + filament::UserVariantFilterMask mVariantFilter = {}; +}; + +} // namespace filamat + +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; + +#endif diff --git a/ios/include/filamat/Package.h b/ios/include/filamat/Package.h new file mode 100644 index 00000000..93e74a58 --- /dev/null +++ b/ios/include/filamat/Package.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMAT_PACKAGE_H +#define TNT_FILAMAT_PACKAGE_H + +#include +#include +#include + +#include +#include + +#include + +namespace filamat { + +class UTILS_PUBLIC Package { +public: + Package() = default; + + // Regular constructor + explicit Package(size_t size) : mSize(size) { + mPayload = new uint8_t[size]; + } + + Package(const void* src, size_t size) : Package(size) { + memcpy(mPayload, src, size); + } + + // Move Constructor + Package(Package&& other) noexcept : mPayload(other.mPayload), mSize(other.mSize), + mValid(other.mValid) { + other.mPayload = nullptr; + other.mSize = 0; + other.mValid = false; + } + + // Move assignment + Package& operator=(Package&& other) noexcept { + std::swap(mPayload, other.mPayload); + std::swap(mSize, other.mSize); + std::swap(mValid, other.mValid); + return *this; + } + + // Copy assignment operator disallowed. + Package& operator=(const Package& other) = delete; + + // Copy constructor disallowed. + Package(const Package& other) = delete; + + ~Package() { + delete[] mPayload; + } + + uint8_t* getData() const noexcept { + return mPayload; + } + + size_t getSize() const noexcept { + return mSize; + } + + uint8_t* getEnd() const noexcept { + return mPayload + mSize; + } + + void setValid(bool valid) noexcept { + mValid = valid; + } + + bool isValid() const noexcept { + return mValid; + } + + static Package invalidPackage() { + Package package(0); + package.setValid(false); + return package; + } + +private: + uint8_t* mPayload = nullptr; + size_t mSize = 0; + bool mValid = true; +}; + +} // namespace filamat +#endif diff --git a/ios/include/filament-iblprefilter/IBLPrefilterContext.h b/ios/include/filament-iblprefilter/IBLPrefilterContext.h new file mode 100644 index 00000000..15cd6810 --- /dev/null +++ b/ios/include/filament-iblprefilter/IBLPrefilterContext.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_IBL_PREFILTER_IBLPREFILTER_H +#define TNT_IBL_PREFILTER_IBLPREFILTER_H + +#include +#include + +#include + +namespace filament { +class Engine; +class View; +class Scene; +class Renderer; +class Material; +class MaterialInstance; +class VertexBuffer; +class IndexBuffer; +class Camera; +class Texture; +} // namespace filament + +/** + * IBLPrefilterContext creates and initializes GPU state common to all environment map filters + * supported. Typically, only one instance per filament Engine of this object needs to exist. + * + * Usage Example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * + * IBLPrefilterContext context(engine); + * IBLPrefilterContext::SpecularFilter filter(context); + * Texture* texture = filter(environment_cubemap); + * + * IndirectLight* indirectLight = IndirectLight::Builder() + * .reflections(texture) + * .build(engine); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +class UTILS_PUBLIC IBLPrefilterContext { +public: + + /** + * Creates an IBLPrefilter context. + * @param engine filament engine to use + */ + explicit IBLPrefilterContext(filament::Engine& engine); + + /** + * Destroys all GPU resources created during initialization. + */ + ~IBLPrefilterContext() noexcept; + + // not copyable + IBLPrefilterContext(IBLPrefilterContext const&) = delete; + IBLPrefilterContext& operator=(IBLPrefilterContext const&) = delete; + + // movable + IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept; + IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs) noexcept; + + // ------------------------------------------------------------------------------------------- + + /** + * EquirectangularToCubemap is use to convert an equirectangluar image to a cubemap. + */ + class EquirectangularToCubemap { + public: + /** + * Creates a EquirectangularToCubemap processor. + * @param context IBLPrefilterContext to use + */ + explicit EquirectangularToCubemap(IBLPrefilterContext& context); + + /** + * Destroys all GPU resources created during initialization. + */ + ~EquirectangularToCubemap() noexcept; + + EquirectangularToCubemap(EquirectangularToCubemap const&) = delete; + EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete; + EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept; + EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs) noexcept; + + /** + * Converts an equirectangular image to a cubemap. + * @param equirectangular Texture to convert to a cubemap. + * - Can't be null. + * - Must be a 2d texture + * - Must have equirectangular geometry, that is width == 2*height. + * - Must be allocated with all mip levels. + * - Must be SAMPLEABLE + * @param outCubemap Output cubemap. If null the texture is automatically created + * with default parameters (size of 256 with 5 levels). + * - Must be a cubemap + * - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits + * @return returns outCubemap + */ + filament::Texture* operator()( + filament::Texture const* equirectangular, + filament::Texture* outCubemap = nullptr); + + private: + IBLPrefilterContext& mContext; + filament::Material* mEquirectMaterial = nullptr; + }; + + + /** + * SpecularFilter is a GPU based implementation of the specular probe pre-integration filter. + * An instance of SpecularFilter is needed per filter configuration. A filter configuration + * contains the filter's kernel and sample count. + */ + class SpecularFilter { + public: + enum class Kernel : uint8_t { + D_GGX, // Trowbridge-reitz distribution + }; + + /** + * Filter configuration. + */ + struct Config { + uint16_t sampleCount = 1024u; //!< filter sample count (max 2048) + uint8_t levelCount = 5u; //!< number of roughness levels + Kernel kernel = Kernel::D_GGX; //!< filter kernel + }; + + /** + * Filtering options for the current environment. + */ + struct Options { + float hdrLinear = 1024.0f; //!< no HDR compression up to this value + float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax + float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs. + bool generateMipmap = true; //!< set to false if the environment map already has mipmaps + }; + + /** + * Creates a SpecularFilter processor. + * @param context IBLPrefilterContext to use + * @param config Configuration of the filter + */ + SpecularFilter(IBLPrefilterContext& context, Config config); + + /** + * Creates a filter with the default configuration. + * @param context IBLPrefilterContext to use + */ + explicit SpecularFilter(IBLPrefilterContext& context); + + /** + * Destroys all GPU resources created during initialization. + */ + ~SpecularFilter() noexcept; + + SpecularFilter(SpecularFilter const&) = delete; + SpecularFilter& operator=(SpecularFilter const&) = delete; + SpecularFilter(SpecularFilter&& rhs) noexcept; + SpecularFilter& operator=(SpecularFilter&& rhs) noexcept; + + /** + * Generates a prefiltered cubemap. + * @param options Options for this environment + * @param environmentCubemap Environment cubemap (input). Can't be null. + * This cubemap must be SAMPLEABLE and must have all its + * levels allocated. If Options.generateMipmap is true, + * the mipmap levels will be overwritten, otherwise + * it is assumed that all levels are correctly initialized. + * @param outReflectionsTexture Output prefiltered texture or, if null, it is + * automatically created with some default parameters. + * outReflectionsTexture must be a cubemap, it must have + * at least COLOR_ATTACHMENT and SAMPLEABLE usages and at + * least the same number of levels than requested by Config. + * @return returns outReflectionsTexture + */ + filament::Texture* operator()(Options options, + filament::Texture const* environmentCubemap, + filament::Texture* outReflectionsTexture = nullptr); + + /** + * Generates a prefiltered cubemap. + * @param environmentCubemap Environment cubemap (input). Can't be null. + * This cubemap must be SAMPLEABLE and must have all its + * levels allocated. All mipmap levels will be overwritten. + * @param outReflectionsTexture Output prefiltered texture or, if null, it is + * automatically created with some default parameters. + * outReflectionsTexture must be a cubemap, it must have + * at least COLOR_ATTACHMENT and SAMPLEABLE usages and at + * least the same number of levels than requested by Config. + * @return returns outReflectionsTexture + */ + filament::Texture* operator()( + filament::Texture const* environmentCubemap, + filament::Texture* outReflectionsTexture = nullptr); + + // TODO: option for progressive filtering + + // TODO: add a callback for when the processing is done? + + private: + filament::Texture* createReflectionsTexture(); + IBLPrefilterContext& mContext; + filament::Material* mKernelMaterial = nullptr; + filament::Texture* mKernelTexture = nullptr; + uint32_t mSampleCount = 0u; + uint8_t mLevelCount = 1u; + }; + +private: + friend class Filter; + filament::Engine& mEngine; + filament::Renderer* mRenderer{}; + filament::Scene* mScene{}; + filament::VertexBuffer* mVertexBuffer{}; + filament::IndexBuffer* mIndexBuffer{}; + filament::Camera* mCamera{}; + utils::Entity mFullScreenQuadEntity{}; + utils::Entity mCameraEntity{}; + filament::View* mView{}; + filament::Material* mIntegrationMaterial{}; +}; + +#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H diff --git a/ios/include/filament/ColorGrading.h b/ios/include/filament/ColorGrading.h index 101efa3e..2cbad944 100644 --- a/ios/include/filament/ColorGrading.h +++ b/ios/include/filament/ColorGrading.h @@ -103,6 +103,7 @@ class FColorGrading; class UTILS_PUBLIC ColorGrading : public FilamentAPI { struct BuilderDetails; public: + enum class QualityLevel : uint8_t { LOW, MEDIUM, @@ -110,6 +111,12 @@ public: ULTRA }; + enum class LutFormat : uint8_t { + INTEGER, //!< 10 bits per component + FLOAT, //!< 16 bits per component (10 bits mantissa precision) + }; + + /** * List of available tone-mapping operators. * @@ -140,6 +147,7 @@ public: * 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium * quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit * LUT, and a ultra quality will use a 64x64x64 16 bit LUT. + * This overrides the values set by format() and dimensions(). * * The default quality is medium. * @@ -149,6 +157,30 @@ public: */ Builder& quality(QualityLevel qualityLevel) noexcept; + /** + * When color grading is implemented using a 3D LUT, this sets the texture format of + * of the LUT. This overrides the value set by quality(). + * + * The default is INTEGER + * + * @param format The desired format of the 3D LUT. + * + * @return This Builder, for chaining calls + */ + Builder& format(LutFormat format) noexcept; + + /** + * When color grading is implemented using a 3D LUT, this sets the dimension of the LUT. + * This overrides the value set by quality(). + * + * The default is 32 + * + * @param dim The desired dimension of the LUT. Between 16 and 64. + * + * @return This Builder, for chaining calls + */ + Builder& dimensions(uint8_t dim) noexcept; + /** * Selects the tone mapping operator to apply to the HDR color buffer as the last * operation of the color grading post-processing step. diff --git a/ios/include/filament/FilamentAPI.h b/ios/include/filament/FilamentAPI.h index e0f35cab..2925aca4 100644 --- a/ios/include/filament/FilamentAPI.h +++ b/ios/include/filament/FilamentAPI.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_FILAMENTAPI_H #include +#include #include @@ -52,39 +53,8 @@ public: static void operator delete[](void*) = delete; }; - -/** - * \privatesection - * BuilderBase is used to hide the implementation details of builders and ensure a higher - * level of backward binary compatibility. - * The actual implementation is in src/FilamentAPI-impl.h" - */ -template -class BuilderBase { -public: - // none of these methods must be implemented inline because it's important that their - // implementation be hidden from the public headers. - template - explicit BuilderBase(ARGS&& ...) noexcept; - BuilderBase() noexcept; - ~BuilderBase() noexcept; - BuilderBase(BuilderBase const& rhs) noexcept; - BuilderBase& operator = (BuilderBase const& rhs) noexcept; - - // move ctor and copy operator can be implemented inline and don't need to be exported - BuilderBase(BuilderBase&& rhs) noexcept : mImpl(rhs.mImpl) { rhs.mImpl = nullptr; } - BuilderBase& operator = (BuilderBase&& rhs) noexcept { - auto temp = mImpl; - mImpl = rhs.mImpl; - rhs.mImpl = temp; - return *this; - } - -protected: - T* mImpl = nullptr; - inline T* operator->() noexcept { return mImpl; } - inline T const* operator->() const noexcept { return mImpl; } -}; +template +using BuilderBase = utils::PrivateImplementation; } // namespace filament diff --git a/ios/include/filament/Frustum.h b/ios/include/filament/Frustum.h index 5d496e57..a19a92bb 100644 --- a/ios/include/filament/Frustum.h +++ b/ios/include/filament/Frustum.h @@ -58,30 +58,6 @@ public: */ explicit Frustum(const math::mat4f& pv); - /** - * Creates a frustum from 8 corner coordinates. - * @param corners the corners of the frustum - * - * The corners should be specified in this order: - * 0. far bottom left - * 1. far bottom right - * 2. far top left - * 3. far top right - * 4. near bottom left - * 5. near bottom right - * 6. near top left - * 7. near top right - * - * 2----3 - * /| /| - * 6----7 | - * | 0--|-1 far - * |/ |/ / - * 4----5 near - * - */ - explicit Frustum(const math::float3 corners[8]); - /** * Sets the frustum from the given projection matrix * @param pv a 4x4 projection matrix diff --git a/ios/include/filament/MaterialEnums.h b/ios/include/filament/MaterialEnums.h index c4ef79b5..3bc8a929 100644 --- a/ios/include/filament/MaterialEnums.h +++ b/ios/include/filament/MaterialEnums.h @@ -27,7 +27,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 17; +static constexpr size_t MATERIAL_VERSION = 21; /** * Supported shading models @@ -136,10 +136,22 @@ enum VertexAttribute : uint8_t { CUSTOM6 = 14, CUSTOM7 = 15, + // Aliases for legacy vertex morphing. + // See RenderableManager::Builder::morphing(). + MORPH_POSITION_0 = CUSTOM0, + MORPH_POSITION_1 = CUSTOM1, + MORPH_POSITION_2 = CUSTOM2, + MORPH_POSITION_3 = CUSTOM3, + MORPH_TANGENTS_0 = CUSTOM4, + MORPH_TANGENTS_1 = CUSTOM5, + MORPH_TANGENTS_2 = CUSTOM6, + MORPH_TANGENTS_3 = CUSTOM7, + // this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT }; -static constexpr size_t MAX_MORPH_TARGETS = 128; // this is limited by filament::CONFIG_MAX_MORPH_TARGET_COUNT +static constexpr size_t MAX_LEGACY_MORPH_TARGETS = 4; +static constexpr size_t MAX_MORPH_TARGETS = 256; // this is limited by filament::CONFIG_MAX_MORPH_TARGET_COUNT static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8; /** @@ -219,6 +231,18 @@ enum class Property : uint8_t { // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; +enum class UserVariantFilterBit : uint32_t { + DIRECTIONAL_LIGHTING = 0x01, + DYNAMIC_LIGHTING = 0x02, + SHADOW_RECEIVER = 0x04, + SKINNING = 0x08, + FOG = 0x10, + VSM = 0x20, + SSR = 0x40, +}; + +using UserVariantFilterMask = uint32_t; + } // namespace filament #endif diff --git a/ios/include/filament/MaterialInstance.h b/ios/include/filament/MaterialInstance.h index fee1ca0d..2cf194fa 100644 --- a/ios/include/filament/MaterialInstance.h +++ b/ios/include/filament/MaterialInstance.h @@ -109,6 +109,9 @@ public: /** * Set a texture as the named parameter * + * Note: Depth textures can't be sampled with a linear filter unless the comparison mode is set + * to COMPARE_TO_TEXTURE. + * * @param name Name of the parameter as defined by Material. Cannot be nullptr. * @param texture Non nullptr Texture object pointer. * @param sampler Sampler parameters. diff --git a/ios/include/filament/MorphTargetBuffer.h b/ios/include/filament/MorphTargetBuffer.h index f3c08770..d96fe6df 100644 --- a/ios/include/filament/MorphTargetBuffer.h +++ b/ios/include/filament/MorphTargetBuffer.h @@ -28,6 +28,9 @@ namespace filament { /** * MorphTargetBuffer is used to hold morphing data (positions and tangents). + * + * Both positions and tangents are required. + * */ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { struct BuilderDetails; @@ -76,30 +79,45 @@ public: /** * Updates the position of morph target at the index. + * + * Both positions and tangents must be provided. + * * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. * @param targetIndex the index of morph target to be updated. * @param weights pointer to at least count positions * @param count number of position elements in positions + * @see setTangentsAt */ - void setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count); + void setPositionsAt(Engine& engine, size_t targetIndex, + math::float3 const* positions, size_t count, size_t offset = 0); /** * Updates the position of morph target at the index. + * + * Both positions and tangents must be provided. + * * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. * @param targetIndex the index of morph target to be updated. * @param weights pointer to at least count positions * @param count number of position elements in positions + * @see setPositionsAt */ - void setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count); + void setPositionsAt(Engine& engine, size_t targetIndex, + math::float4 const* positions, size_t count, size_t offset = 0); /** * Updates the position of morph target at the index. + * + * Both positions and tangents must be provided. + * * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. * @param targetIndex the index of morph target to be updated. * @param tangents pointer to at least count tangents * @param count number of tangent elements in tangents + * @see setTangentsAt */ - void setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count); + void setTangentsAt(Engine& engine, size_t targetIndex, + math::short4 const* tangents, size_t count, size_t offset = 0); /** * Returns the vertex count of this MorphTargetBuffer. diff --git a/ios/include/filament/Options.h b/ios/include/filament/Options.h index b43a5f34..6c750ae9 100644 --- a/ios/include/filament/Options.h +++ b/ios/include/filament/Options.h @@ -321,10 +321,10 @@ struct TemporalAntiAliasingOptions { * @see setScreenSpaceReflectionsOptions() */ struct ScreenSpaceReflectionsOptions { - float thickness = 0.5f; //!< ray thickness, in world units + float thickness = 0.1f; //!< ray thickness, in world units float bias = 0.01f; //!< bias, in world units, to prevent self-intersections float maxDistance = 3.0f; //!< maximum distance, in world units, to raycast - float stride = 1.0f; //!< stride, in texels, for samples along the ray. + float stride = 2.0f; //!< stride, in texels, for samples along the ray. bool enabled = false; }; diff --git a/ios/include/filament/RenderableManager.h b/ios/include/filament/RenderableManager.h index 47f4fc56..51b30e87 100644 --- a/ios/include/filament/RenderableManager.h +++ b/ios/include/filament/RenderableManager.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -41,7 +42,6 @@ class Engine; class IndexBuffer; class Material; class MaterialInstance; -class MorphTargetBuffer; class Renderer; class SkinningBuffer; class VertexBuffer; @@ -300,12 +300,45 @@ public: Builder& skinning(size_t boneCount) noexcept; //!< \overload /** - * Controls if the renderable has vertex morphing targets, false by default. + * Controls if the renderable has vertex morphing targets, zero by default. This is + * required to enable GPU morphing. + * + * Filament supports two morphing modes: standard (default) and legacy. + * + * For standard morphing, A MorphTargetBuffer must be created and provided via + * RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to + * \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. + * + * For legacy morphing, the attached VertexBuffer must provide data in the + * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only + * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must + * be enabled on the material definition: either via the legacyMorphing material attribute + * or by calling filamat::MaterialBuilder::useLegacyMorphing(). * * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis * to advance the animation. */ - Builder& morphing(bool enable) noexcept; + Builder& morphing(size_t targetCount) noexcept; + + /** + * Specifies the morph target buffer for a primitive. + * + * The morph target buffer must have an associated renderable and geometry. Two conditions + * must be met: + * 1. The number of morph targets in the buffer must equal the renderable's morph target + * count. + * 2. The vertex count of each morph target must equal the geometry's vertex count. + * + * @param level the level of detail (lod), only 0 can be specified + * @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor + * @param morphTargetBuffer specifies the morph target buffer + * @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices) + * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3) + */ + Builder& morphing(uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept; + inline Builder& morphing(uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer) noexcept; /** * Sets an ordering index for blended primitives that all live at the same Z value. @@ -315,6 +348,18 @@ public: */ Builder& blendOrder(size_t primitiveIndex, uint16_t order) noexcept; + /** + * Specifies the number of draw instance of this renderable. The default is 1 instance and + * the maximum number of instances allowed is 65535. 0 is invalid. + * All instances are culled using the same bounding box, so care must be taken to make + * sure all instances render inside the specified bounding box. + * The material can use getInstanceIndex() in the vertex shader to get the instance index and + * possibly adjust the position or transform. + * + * @param instanceCount the number of instances silently clamped between 1 and 65535. + */ + Builder& instances(size_t instanceCount) noexcept; + /** * Adds the Renderable component to an entity. * @@ -349,6 +394,11 @@ public: MaterialInstance const* materialInstance = nullptr; PrimitiveType type = PrimitiveType::TRIANGLES; uint16_t blendOrder = 0; + struct { + MorphTargetBuffer* buffer = nullptr; + size_t offset = 0; + size_t count = 0; + } morphing; }; }; @@ -443,28 +493,45 @@ public: * Updates the bone transforms in the range [offset, offset + boneCount). * The bones must be pre-allocated using Builder::skinning(). */ - void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept; - void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept; //!< \overload + void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0); + void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0); //!< \overload /** * Associates a SkinningBuffer to a renderable instance */ void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer, - size_t count, size_t offset) noexcept; + size_t count, size_t offset); /** * Updates the vertex morphing weights on a renderable, all zeroes by default. * - * The renderable must be built with morphing enabled, see Builder::morphing(). + * The renderable must be built with morphing enabled, see Builder::morphing(). In legacy + * morphing mode, only the first 4 weights are considered. + * + * @param instance Instance of the component obtained from getInstance(). + * @param weights Pointer to morph target weights to be update. + * @param count Number of morph target weights. + * @param offset Index of the first first morph target weight to set at instance. */ - void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept; - + void setMorphWeights(Instance instance, + float const* weights, size_t count, size_t offset = 0); /** * Associates a MorphTargetBuffer to the given primitive. */ - void setMorphTargetBufferAt(Instance instance, - size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept; + void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count); + + /** + * Utility method to change a MorphTargetBuffer to the given primitive + */ + inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer); + + /** + * Gets the number of morphing in the given entity. + */ + size_t getMorphTargetCount(Instance instance) const noexcept; /** * Gets the bounding box used for frustum culling. @@ -568,6 +635,18 @@ public: size_t stride = sizeof(VECTOR)) noexcept; }; +RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer) noexcept { + return morphing(level, primitiveIndex, morphTargetBuffer, 0, + morphTargetBuffer->getVertexCount()); +} + +void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* morphTargetBuffer) { + setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, + morphTargetBuffer->getVertexCount()); +} + template Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count, size_t stride) noexcept { diff --git a/ios/include/filament/Scene.h b/ios/include/filament/Scene.h index 7ef5c22f..e89fc9a7 100644 --- a/ios/include/filament/Scene.h +++ b/ios/include/filament/Scene.h @@ -22,6 +22,7 @@ #include #include +#include namespace utils { class Entity; @@ -156,6 +157,15 @@ public: * @return Whether the given entity is in the Scene. */ bool hasEntity(utils::Entity entity) const noexcept; + + /** + * Invokes user functor on each entity in the scene. + * + * It is not allowed to add or remove an entity from the scene within the functor. + * + * @param functor User provided functor called for each entity in the scene + */ + void forEach(utils::Invocable&& functor) const noexcept; }; } // namespace filament diff --git a/ios/include/filament/TransformManager.h b/ios/include/filament/TransformManager.h index f690e766..9afa6897 100644 --- a/ios/include/filament/TransformManager.h +++ b/ios/include/filament/TransformManager.h @@ -70,13 +70,19 @@ class UTILS_PUBLIC TransformManager : public FilamentAPI { public: using Instance = utils::EntityInstance; - class children_iterator : std::iterator { + class children_iterator { friend class FTransformManager; TransformManager const& mManager; Instance mInstance; children_iterator(TransformManager const& mgr, Instance instance) noexcept : mManager(mgr), mInstance(instance) { } public: + using value_type = Instance; + using difference_type = ptrdiff_t; + using pointer = Instance*; + using reference = Instance&; + using iterator_category = std::forward_iterator_tag; + children_iterator& operator++(); children_iterator operator++(int) { // NOLINT diff --git a/ios/include/filament/View.h b/ios/include/filament/View.h index 1083560c..14c7b838 100644 --- a/ios/include/filament/View.h +++ b/ios/include/filament/View.h @@ -674,7 +674,7 @@ public: * @tparam method Method to call on T (e.g.: &Foo::bar) * @param x Horizontal coordinate to query in the viewport with origin on the left. * @param y Vertical coordinate to query on the viewport with origin at the bottom. - * @param data A pointer to an instance of T + * @param instance A pointer to an instance of T * @param handler Handler to dispatch the callback or nullptr for the default handler. */ template @@ -694,7 +694,7 @@ public: * @tparam method Method to call on T (e.g.: &Foo::bar) * @param x Horizontal coordinate to query in the viewport with origin on the left. * @param y Vertical coordinate to query on the viewport with origin at the bottom. - * @param data An instance of T + * @param instance An instance of T * @param handler Handler to dispatch the callback or nullptr for the default handler. */ template diff --git a/ios/include/filameshio/MeshReader.h b/ios/include/filameshio/MeshReader.h new file mode 100644 index 00000000..f9da44bd --- /dev/null +++ b/ios/include/filameshio/MeshReader.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_FILAMESHIO_MESHREADER_H +#define TNT_FILAMENT_FILAMESHIO_MESHREADER_H + +#include +#include +#include + +namespace filament { + class Engine; + class VertexBuffer; + class IndexBuffer; + class MaterialInstance; +} + +namespace utils { + class Path; +} + +namespace filamesh { + + +/** + * This API can be used to read meshes stored in the "filamesh" format produced + * by the command line tool of the same name. This file format is documented in + * "docs/filamesh.md" in the Filament distribution. + */ +class UTILS_PUBLIC MeshReader { +public: + using Callback = void(*)(void* buffer, size_t size, void* user); + + // Class to track material instances + class MaterialRegistry { + public: + MaterialRegistry(); + MaterialRegistry(const MaterialRegistry& rhs); + MaterialRegistry& operator=(const MaterialRegistry& rhs); + ~MaterialRegistry(); + MaterialRegistry(MaterialRegistry&&); + MaterialRegistry& operator=(MaterialRegistry&&); + + filament::MaterialInstance* getMaterialInstance(const utils::CString& name); + + void registerMaterialInstance(const utils::CString& name, + filament::MaterialInstance* materialInstance); + + void unregisterMaterialInstance(const utils::CString& name); + + void unregisterAll(); + + std::size_t numRegistered() const noexcept; + + void getRegisteredMaterials(filament::MaterialInstance** materialList, + utils::CString* materialNameList) const; + + void getRegisteredMaterials(filament::MaterialInstance** materialList) const; + + void getRegisteredMaterialNames(utils::CString* materialNameList) const; + + private: + struct MaterialRegistryImpl; + MaterialRegistryImpl* mImpl; + }; + + struct Mesh { + utils::Entity renderable; + filament::VertexBuffer* vertexBuffer = nullptr; + filament::IndexBuffer* indexBuffer = nullptr; + }; + + /** + * Loads a filamesh renderable from the specified file. The material registry + * can be used to provide named materials. If a material found in the filamesh + * file cannot be matched to a material in the registry, a default material is + * used instead. The default material can be overridden by adding a material + * named "DefaultMaterial" to the registry. + */ + static Mesh loadMeshFromFile(filament::Engine* engine, + const utils::Path& path, + MaterialRegistry& materials); + + /** + * Loads a filamesh renderable from an in-memory buffer. The material registry + * can be used to provide named materials. If a material found in the filamesh + * file cannot be matched to a material in the registry, a default material is + * used instead. The default material can be overridden by adding a material + * named "DefaultMaterial" to the registry. + */ + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + MaterialRegistry& materials); + + /** + * Loads a filamesh renderable from an in-memory buffer. The material registry + * can be used to provide named materials. All the primitives of the decoded + * renderable are assigned the specified default material. + */ + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + filament::MaterialInstance* defaultMaterial); +}; + +} // namespace filamesh + +#endif // TNT_FILAMENT_FILAMESHIO_MESHREADER_H diff --git a/ios/include/geometry/SurfaceOrientation.h b/ios/include/geometry/SurfaceOrientation.h new file mode 100644 index 00000000..e9ad75bb --- /dev/null +++ b/ios/include/geometry/SurfaceOrientation.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_GEOMETRY_SURFACEORIENTATION_H +#define TNT_GEOMETRY_SURFACEORIENTATION_H + +#include +#include +#include + +#include + +namespace filament { + +/** + * Mesh-related utilities. + */ +namespace geometry { + +struct OrientationBuilderImpl; +struct OrientationImpl; + +/** + * The surface orientation helper can be used to populate Filament-style TANGENTS buffers. + */ +class UTILS_PUBLIC SurfaceOrientation { +public: + + /** + * The Builder is used to construct an immutable surface orientation helper. + * + * Clients provide pointers into their own data, which is synchronously consumed during build(). + * At a minimum, clients must supply a vertex count. They can supply data in any of the + * following combinations: + * + * 1. normals only ........................... not recommended, selects arbitrary orientation + * 2. normals + tangents ..................... sign of W determines bitangent orientation + * 3. normals + uvs + positions + indices .... selects Lengyel’s Method + * 4. positions + indices .................... generates normals for flat shading only + * + * Additionally, the client-side data has the following type constraints: + * + * - Normals must be float3 + * - Tangents must be float4 + * - UVs must be float2 + * - Positions must be float3 + * - Triangles must be uint3 or ushort3 + * + * Currently, mikktspace is not supported because it requires re-indexing the mesh. Instead + * we use the method described by Eric Lengyel in "Foundations of Game Engine Development" + * (Volume 2, Chapter 7). + */ + class Builder { + public: + Builder() noexcept; + ~Builder() noexcept; + Builder(Builder&& that) noexcept; + Builder& operator=(Builder&& that) noexcept; + + /** + * This attribute is required. + */ + Builder& vertexCount(size_t vertexCount) noexcept; + + Builder& normals(const filament::math::float3*, size_t stride = 0) noexcept; + Builder& tangents(const filament::math::float4*, size_t stride = 0) noexcept; + Builder& uvs(const filament::math::float2*, size_t stride = 0) noexcept; + Builder& positions(const filament::math::float3*, size_t stride = 0) noexcept; + + Builder& triangleCount(size_t triangleCount) noexcept; + Builder& triangles(const filament::math::uint3*) noexcept; + Builder& triangles(const filament::math::ushort3*) noexcept; + + /** + * Generates quats or returns null if the submitted data is an incomplete combination. + */ + SurfaceOrientation* build(); + + private: + OrientationBuilderImpl* mImpl; + Builder(const Builder&) = delete; + Builder& operator=(const Builder&) = delete; + }; + + ~SurfaceOrientation() noexcept; + SurfaceOrientation(SurfaceOrientation&& that) noexcept; + SurfaceOrientation& operator=(SurfaceOrientation&& that) noexcept; + + /** + * Returns the vertex count. + */ + size_t getVertexCount() const noexcept; + + /** + * Converts quaternions into the desired output format and writes up to "quatCount" + * to the given output pointer. Normally quatCount should be equal to the vertex count. + * The optional stride is the desired quat-to-quat stride in bytes. + * @{ + */ + void getQuats(filament::math::quatf* out, size_t quatCount, size_t stride = 0) const noexcept; + void getQuats(filament::math::short4* out, size_t quatCount, size_t stride = 0) const noexcept; + void getQuats(filament::math::quath* out, size_t quatCount, size_t stride = 0) const noexcept; + /** + * @} + */ + +private: + SurfaceOrientation(OrientationImpl*) noexcept; + SurfaceOrientation(const SurfaceOrientation&) = delete; + SurfaceOrientation& operator=(const SurfaceOrientation&) = delete; + OrientationImpl* mImpl; + friend struct OrientationBuilderImpl; +}; + +} // namespace geometry +} // namespace filament + +#endif // TNT_GEOMETRY_SURFACEORIENTATION_H diff --git a/ios/include/geometry/Transcoder.h b/ios/include/geometry/Transcoder.h new file mode 100644 index 00000000..0c77c941 --- /dev/null +++ b/ios/include/geometry/Transcoder.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_GEOMETRY_TRANSCODER_H +#define TNT_GEOMETRY_TRANSCODER_H + +#include + +#include +#include + +namespace filament { +namespace geometry { + +enum class ComponentType { + BYTE, //!< If normalization is enabled, this maps from [-127,127] to [-1,+1] + UBYTE, //!< If normalization is enabled, this maps from [0,255] to [0, +1] + SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1] + USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1] + HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits. +}; + +/** + * Creates a function object that can convert vertex attribute data into tightly packed floats. + * + * This is especially useful for 3-component formats which are not supported by all backends. + * e.g. The Vulkan minspec includes float3 but not short3. + * + * Usage Example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * using filament::geometry::Transcoder; + * using filament::geometry::ComponentType; + * + * Transcoder transcode({ + * .componentType = ComponentType::BYTE, + * .normalized = true, + * .componentCount = 3, + * .inputStrideBytes = 0 + * }); + * + * transcode(outputPtr, inputPtr, count); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The interpretation of signed normalized data is consistent with Vulkan and OpenGL ES 3.0+. + * Note that this slightly differs from earlier versions of OpenGL ES. For example, a signed byte + * value of -127 maps exactly to -1.0f under ES3 and VK rules, but not ES2. + */ +class UTILS_PUBLIC Transcoder { +public: + /** + * Describes the format of all input data that get passed to this transcoder object. + */ + struct Config { + ComponentType componentType; + bool normalized; + uint32_t componentCount; + uint32_t inputStrideBytes = 0; //!< If stride is 0, the transcoder assumes tight packing. + }; + + /** + * Creates an immutable function object with the specified configuration. + * + * The config is not passed by const reference to allow for type inference at the call site. + */ + Transcoder(Config config) noexcept : mConfig(config) {} + + /** + * Converts arbitrary data into tightly packed 32-bit floating point values. + * + * If target is non-null, writes up to "count" items into target and returns the number of bytes + * actually written. + * + * If target is null, returns the number of bytes required. + * + * @param target Client owned area to write into, or null for a size query + * @param source Pointer to the data to read from (does not get retained) + * @param count The maximum number of items to write (i.e. number of float3 values, not bytes) + * @return Number of bytes required to contain "count" items after conversion to packed floats + * + */ + size_t operator()(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, + size_t count) const noexcept; + +private: + const Config mConfig; +}; + +} // namespace geometry +} // namespace filament + +#endif // TNT_GEOMETRY_TRANSCODER_H diff --git a/ios/include/gltfio/Animator.h b/ios/include/gltfio/Animator.h index 53695d5c..ab47f572 100644 --- a/ios/include/gltfio/Animator.h +++ b/ios/include/gltfio/Animator.h @@ -80,6 +80,11 @@ private: Animator(FFilamentAsset* asset, FFilamentInstance* instance); ~Animator(); + + Animator(const Animator& animator) = delete; + Animator(Animator&& animator) = delete; + Animator& operator=(const Animator&) = delete; + AnimatorImpl* mImpl; }; diff --git a/ios/include/gltfio/AssetLoader.h b/ios/include/gltfio/AssetLoader.h index 4fede91f..81feaeb2 100644 --- a/ios/include/gltfio/AssetLoader.h +++ b/ios/include/gltfio/AssetLoader.h @@ -94,9 +94,6 @@ struct AssetConfiguration { * // Load buffers and textures from disk. * ResourceLoader({engine, ".", true}).loadResources(asset); * - * // Obtain the simple animation interface. - * Animator* animator = asset->getAnimator(); - * * // Free the glTF hierarchy as it is no longer needed. * asset->releaseSourceData(); * @@ -105,8 +102,8 @@ struct AssetConfiguration { * * // Execute the render loop and play the first animation. * do { - * animator->applyAnimation(0, time); - * animator->updateBoneMatrices(); + * asset->getAnimator()->applyAnimation(0, time); + * asset->getAnimator()->updateBoneMatrices(); * if (renderer->beginFrame(swapChain)) { * renderer->render(view); * renderer->endFrame(); diff --git a/ios/include/gltfio/FilamentAsset.h b/ios/include/gltfio/FilamentAsset.h index 2b0a6586..6078aa6c 100644 --- a/ios/include/gltfio/FilamentAsset.h +++ b/ios/include/gltfio/FilamentAsset.h @@ -194,24 +194,68 @@ public: const char* getExtras(utils::Entity entity = {}) const noexcept; /** - * Lazily creates the animation engine or returns it from the cache. + * Returns the animation engine. * * The animator is owned by the asset and should not be manually deleted. - * The first time this is called, it must be called before FilamentAsset::releaseSourceData(). + * Must be called after loadResources or asyncBeginLoad, otherwise returns null. * If the asset is instanced, this returns a "primary" animator that controls all instances. * To animate each instance individually, use \see FilamentInstance. */ - Animator* getAnimator() noexcept; + Animator* getAnimator() const noexcept; /** - * Updates the morphing weights in the given entity. + * Gets the number of skins. */ - void setMorphWeights(utils::Entity entity, const float* weights, size_t count); + size_t getSkinCount() const noexcept; /** - * Gets the number of morphing in the given entity. + * Gets the skin name at skin index. */ - int getMorphTargetCount(utils::Entity entity) noexcept; + const char* getSkinNameAt(size_t skinIndex) const noexcept; + + /** + * Gets the number of joints at skin index. + */ + size_t getJointCountAt(size_t skinIndex) const noexcept; + + /** + * Gets joints at skin index. + */ + const utils::Entity* getJointsAt(size_t skinIndex) const noexcept; + + /** + * Gets the morph target name at the given index in the given entity. + */ + const char* getMorphTargetNameAt(utils::Entity entity, size_t targetIndex) const noexcept; + + /** + * Returns the number of morph targets in the given entity. + */ + size_t getMorphTargetCountAt(utils::Entity entity) const noexcept; + + /** + * Returns the number of material variants in the asset. + */ + size_t getMaterialVariantCount() const noexcept; + + /** + * Returns the name of the given material variant, or null if it is out of bounds. + */ + const char* getMaterialVariantName(size_t variantIndex) const noexcept; + + /** + * Applies the given material variant to all primitives that it affects. + * + * This is efficient because it merely swaps around persistent MaterialInstances. If you change + * a material parameter while a certain variant is active, the updated value will be remembered + * after you re-apply that variant. + * + * If the asset is instanced, this affects all instances in the same way. + * To set the variant on an individual instance, use FilamentInstance::applyMaterialVariant. + * + * Ignored if variantIndex is out of bounds. + */ + void applyMaterialVariant(size_t variantIndex) noexcept; /** * Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy @@ -228,7 +272,6 @@ public: * Reclaims CPU-side memory for URI strings, binding lists, and raw animation data. * * This should only be called after ResourceLoader::loadResources(). - * If using Animator, this should be called after getAnimator(). * If this is an instanced asset, this prevents creation of new instances. */ void releaseSourceData() noexcept; diff --git a/ios/include/gltfio/FilamentInstance.h b/ios/include/gltfio/FilamentInstance.h index 007116a7..1f1509e3 100644 --- a/ios/include/gltfio/FilamentInstance.h +++ b/ios/include/gltfio/FilamentInstance.h @@ -57,7 +57,14 @@ public: utils::Entity getRoot() const noexcept; /** - * Lazily creates the animation engine for the instance, or returns it from the cache. + * Applies the given material variant to all primitives in this instance. + * + * Ignored if variantIndex is out of bounds. + */ + void applyMaterialVariant(size_t variantIndex) noexcept; + + /** + * Returns the animation engine for the instance. * * Note that an animator can be obtained either from an individual instance, or from the * originating FilamentAsset. In the latter case, the animation frame is shared amongst all @@ -65,7 +72,6 @@ public: * individual instances. * * The animator is owned by the asset and should not be manually deleted. - * The first time this is called, it must be called before FilamentAsset::releaseSourceData(). */ Animator* getAnimator() noexcept; }; diff --git a/ios/include/gltfio/Image.h b/ios/include/gltfio/Image.h deleted file mode 100644 index c7047311..00000000 --- a/ios/include/gltfio/Image.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// gltfio supports PNG and JPEG, disable all other formats. -#define STBI_NO_BMP -#define STBI_NO_PSD -#define STBI_NO_TGA -#define STBI_NO_GIF -#define STBI_NO_HDR -#define STBI_NO_PIC -#define STBI_NO_PNM - -// For emscripten and Android builds, we never load from the file -// system, so we-opt out of the stdio functionality in stb. -#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) -#define STBI_NO_STDIO -#endif - -#include diff --git a/ios/include/gltfio/MaterialProvider.h b/ios/include/gltfio/MaterialProvider.h index fd3cc224..9353ea9d 100644 --- a/ios/include/gltfio/MaterialProvider.h +++ b/ios/include/gltfio/MaterialProvider.h @@ -34,6 +34,11 @@ enum class AlphaMode : uint8_t { BLEND }; +// The following struct gets hashed so all padding bits should be explicit. +// Tell the compiler to emit a warning if it adds any padding. +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + /** * \struct MaterialKey MaterialProvider.h gltfio/MaterialProvider.h * \brief Small POD structure that specifies the requirements for a glTF material. @@ -88,9 +93,12 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; + uint8_t padding : 5; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected padding."); +static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); + +#pragma clang diagnostic pop bool operator==(const MaterialKey& k1, const MaterialKey& k2); @@ -133,9 +141,10 @@ public: * @param uvmap Output argument that gets populated with a small table that maps from a glTF uv * index to a Filament uv index. * @param label Optional tag that is not a part of the cache key. + * @param extras Optional extras as stringified JSON (not a part of the cache key). Don't store the pointer. */ virtual filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, - const char* label = "material") = 0; + const char* label = "material", const char* extras = nullptr) = 0; /** * Gets a weak reference to the array of cached materials. diff --git a/ios/include/gltfio/math.h b/ios/include/gltfio/math.h new file mode 100644 index 00000000..f94d3cc7 --- /dev/null +++ b/ios/include/gltfio/math.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_MATH_H +#define GLTFIO_MATH_H + +#include +#include +#include +#include +#include + +namespace gltfio { + +template +UTILS_PUBLIC T cubicSpline(const T& vert0, const T& tang0, const T& vert1, const T& tang1, float t) { + float tt = t * t, ttt = tt * t; + float s2 = -2 * ttt + 3 * tt, s3 = ttt - tt; + float s0 = 1 - s2, s1 = s3 - tt + t; + T p0 = vert0; + T m0 = tang0; + T p1 = vert1; + T m1 = tang1; + return s0 * p0 + s1 * m0 * t + s2 * p1 + s3 * m1 * t; +} + +UTILS_PUBLIC inline void decomposeMatrix(const filament::math::mat4f& mat, filament::math::float3* translation, + filament::math::quatf* rotation, filament::math::float3* scale) { + using namespace filament::math; + + // Extract translation. + *translation = mat[3].xyz; + + // Extract upper-left for determinant computation. + const float a = mat[0][0]; + const float b = mat[0][1]; + const float c = mat[0][2]; + const float d = mat[1][0]; + const float e = mat[1][1]; + const float f = mat[1][2]; + const float g = mat[2][0]; + const float h = mat[2][1]; + const float i = mat[2][2]; + const float A = e * i - f * h; + const float B = f * g - d * i; + const float C = d * h - e * g; + + // Extract scale. + const float det(a * A + b * B + c * C); + float scalex = length(float3({a, b, c})); + float scaley = length(float3({d, e, f})); + float scalez = length(float3({g, h, i})); + float3 s = { scalex, scaley, scalez }; + if (det < 0) { + s = -s; + } + *scale = s; + + // Remove scale from the matrix if it is not close to zero. + mat4f clone = mat; + if (std::abs(det) > std::numeric_limits::epsilon()) { + clone[0] /= s.x; + clone[1] /= s.y; + clone[2] /= s.z; + // Extract rotation + *rotation = clone.toQuaternion(); + } else { + // Set to identity if close to zero + *rotation = quatf(1); + } +} + +UTILS_PUBLIC inline filament::math::mat4f composeMatrix(const filament::math::float3& translation, + const filament::math::quatf& rotation, const filament::math::float3& scale) { + float tx = translation[0]; + float ty = translation[1]; + float tz = translation[2]; + float qx = rotation[0]; + float qy = rotation[1]; + float qz = rotation[2]; + float qw = rotation[3]; + float sx = scale[0]; + float sy = scale[1]; + float sz = scale[2]; + return filament::math::mat4f( + (1 - 2 * qy*qy - 2 * qz*qz) * sx, + (2 * qx*qy + 2 * qz*qw) * sx, + (2 * qx*qz - 2 * qy*qw) * sx, + 0.f, + (2 * qx*qy - 2 * qz*qw) * sy, + (1 - 2 * qx*qx - 2 * qz*qz) * sy, + (2 * qy*qz + 2 * qx*qw) * sy, + 0.f, + (2 * qx*qz + 2 * qy*qw) * sz, + (2 * qy*qz - 2 * qx*qw) * sz, + (1 - 2 * qx*qx - 2 * qy*qy) * sz, + 0.f, tx, ty, tz, 1.f); +} + +inline filament::math::mat3f matrixFromUvTransform(const float offset[2], float rotation, + const float scale[2]) { + float tx = offset[0]; + float ty = offset[1]; + float sx = scale[0]; + float sy = scale[1]; + float c = cos(rotation); + float s = sin(rotation); + return filament::math::mat3f(sx * c, sx * s, tx, -sy * s, sy * c, ty, 0.0f, 0.0f, 1.0f); +}; + +} // namespace gltfio + +#endif // GLTFIO_MATH_H diff --git a/ios/include/gltfio/resources/gltfresources.h b/ios/include/gltfio/resources/gltfresources.h new file mode 100644 index 00000000..a820383e --- /dev/null +++ b/ios/include/gltfio/resources/gltfresources.h @@ -0,0 +1,46 @@ +#ifndef GLTFRESOURCES_H_ +#define GLTFRESOURCES_H_ + +#include + +extern "C" { + extern const uint8_t GLTFRESOURCES_PACKAGE[]; + extern int GLTFRESOURCES_LIT_FADE_OFFSET; + extern int GLTFRESOURCES_LIT_FADE_SIZE; + extern int GLTFRESOURCES_LIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_LIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_LIT_MASKED_OFFSET; + extern int GLTFRESOURCES_LIT_MASKED_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_SIZE; + extern int GLTFRESOURCES_UNLIT_FADE_OFFSET; + extern int GLTFRESOURCES_UNLIT_FADE_SIZE; + extern int GLTFRESOURCES_UNLIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_UNLIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_UNLIT_MASKED_OFFSET; + extern int GLTFRESOURCES_UNLIT_MASKED_SIZE; + extern int GLTFRESOURCES_LIT_VOLUME_OFFSET; + extern int GLTFRESOURCES_LIT_VOLUME_SIZE; + extern int GLTFRESOURCES_LIT_TRANSMISSION_OFFSET; + extern int GLTFRESOURCES_LIT_TRANSMISSION_SIZE; + extern int GLTFRESOURCES_LIT_SHEEN_OFFSET; + extern int GLTFRESOURCES_LIT_SHEEN_SIZE; +} +#define GLTFRESOURCES_LIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_FADE_OFFSET) +#define GLTFRESOURCES_LIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_LIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_MASKED_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET) +#define GLTFRESOURCES_UNLIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_FADE_OFFSET) +#define GLTFRESOURCES_UNLIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_UNLIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_MASKED_OFFSET) +#define GLTFRESOURCES_LIT_VOLUME_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_VOLUME_OFFSET) +#define GLTFRESOURCES_LIT_TRANSMISSION_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_TRANSMISSION_OFFSET) +#define GLTFRESOURCES_LIT_SHEEN_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_SHEEN_OFFSET) + +#endif diff --git a/ios/include/gltfio/resources/gltfresources_lite.h b/ios/include/gltfio/resources/gltfresources_lite.h new file mode 100644 index 00000000..8bef16f4 --- /dev/null +++ b/ios/include/gltfio/resources/gltfresources_lite.h @@ -0,0 +1,16 @@ +#ifndef GLTFRESOURCES_LITE_H_ +#define GLTFRESOURCES_LITE_H_ + +#include + +extern "C" { + extern const uint8_t GLTFRESOURCES_LITE_PACKAGE[]; + extern int GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_LITE_LIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_LITE_LIT_FADE_OFFSET; + extern int GLTFRESOURCES_LITE_LIT_FADE_SIZE; +} +#define GLTFRESOURCES_LITE_LIT_OPAQUE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_LITE_LIT_FADE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_FADE_OFFSET) + +#endif diff --git a/ios/include/ibl/Cubemap.h b/ios/include/ibl/Cubemap.h new file mode 100644 index 00000000..2186bdb0 --- /dev/null +++ b/ios/include/ibl/Cubemap.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAP_H +#define IBL_CUBEMAP_H + +#include + +#include + +#include +#include +#include + +#include + +namespace filament { +namespace ibl { + +/** + * Generic cubemap class. It handles writing / reading into the 6 faces of a cubemap. + * + * Seamless trilinear filtering is handled. + * + * This class doesn't own the face data, it's just a "view" on the 6 images. + * + * @see CubemapUtils + * + */ +class UTILS_PUBLIC Cubemap { +public: + + /** + * Initialize the cubemap with a given size, but no face is set and no memory is allocated. + * + * Usually Cubemaps are created using CubemapUtils. + * + * @see CubemapUtils + */ + explicit Cubemap(size_t dim); + + Cubemap(Cubemap&&) = default; + Cubemap& operator=(Cubemap&&) = default; + + ~Cubemap(); + + + enum class Face : uint8_t { + PX = 0, // left +----+ + NX, // right | PY | + PY, // bottom +----+----+----+----+ + NY, // top | NX | PZ | PX | NZ | + PZ, // back +----+----+----+----+ + NZ // front | NY | + // +----+ + }; + + using Texel = filament::math::float3; + + + //! releases all images and reset the cubemap size + void resetDimensions(size_t dim); + + //! assigns an image to a face. + void setImageForFace(Face face, const Image& image); + + //! retrieves the image attached to a face + inline const Image& getImageForFace(Face face) const; + + //! retrieves the image attached to a face + inline Image& getImageForFace(Face face); + + //! computes the center of a pixel at coordinate x, y + static inline filament::math::float2 center(size_t x, size_t y); + + //! computes a direction vector from a face and a location of the center of pixel in an Image + inline filament::math::float3 getDirectionFor(Face face, size_t x, size_t y) const; + + //! computes a direction vector from a face and a location in pixel in an Image + inline filament::math::float3 getDirectionFor(Face face, float x, float y) const; + + //! samples the cubemap at the given direction using nearest neighbor filtering + inline Texel const& sampleAt(const filament::math::float3& direction) const; + + //! samples the cubemap at the given direction using bilinear filtering + inline Texel filterAt(const filament::math::float3& direction) const; + + //! samples an image at the given location in pixel using bilinear filtering + static Texel filterAt(const Image& image, float x, float y); + static Texel filterAtCenter(const Image& image, size_t x, size_t y); + + //! samples two cubemaps in a given direction and lerps the result by a given lerp factor + static Texel trilinearFilterAt(const Cubemap& c0, const Cubemap& c1, float lerp, + const filament::math::float3& direction); + + //! reads a texel at a given address + inline static const Texel& sampleAt(void const* data) { + return *static_cast(data); + } + + //! writes a texel at a given address + inline static void writeAt(void* data, const Texel& texel) { + *static_cast(data) = texel; + } + + //! returns the size of the cubemap in pixels + size_t getDimensions() const; + + /** + * Prepares a cubemap for seamless access to its faces. + * + * @warning All faces of the cubemap must be backed-up by the same Image, and must already + * be spaced by 2 lines/rows. + */ + void makeSeamless(); + + struct Address { + Face face; + float s = 0; + float t = 0; + }; + + //! returns the face and texture coordinates of the given direction + static Address getAddressFor(const filament::math::float3& direction); + +private: + size_t mDimensions = 0; + float mScale = 1; + float mUpperBound = 0; + Image mFaces[6]; +}; + +// ------------------------------------------------------------------------------------------------ + +inline const Image& Cubemap::getImageForFace(Face face) const { + return mFaces[int(face)]; +} + +inline Image& Cubemap::getImageForFace(Face face) { + return mFaces[int(face)]; +} + +inline filament::math::float2 Cubemap::center(size_t x, size_t y) { + return { x + 0.5f, y + 0.5f }; +} + +inline filament::math::float3 Cubemap::getDirectionFor(Face face, size_t x, size_t y) const { + return getDirectionFor(face, x + 0.5f, y + 0.5f); +} + +inline filament::math::float3 Cubemap::getDirectionFor(Face face, float x, float y) const { + // map [0, dim] to [-1,1] with (-1,-1) at bottom left + float cx = (x * mScale) - 1; + float cy = 1 - (y * mScale); + + filament::math::float3 dir; + const float l = std::sqrt(cx * cx + cy * cy + 1); + switch (face) { + case Face::PX: dir = { 1, cy, -cx }; break; + case Face::NX: dir = { -1, cy, cx }; break; + case Face::PY: dir = { cx, 1, -cy }; break; + case Face::NY: dir = { cx, -1, cy }; break; + case Face::PZ: dir = { cx, cy, 1 }; break; + case Face::NZ: dir = { -cx, cy, -1 }; break; + } + return dir * (1 / l); +} + +inline Cubemap::Texel const& Cubemap::sampleAt(const filament::math::float3& direction) const { + Cubemap::Address addr(getAddressFor(direction)); + const size_t x = std::min(size_t(addr.s * mDimensions), mDimensions - 1); + const size_t y = std::min(size_t(addr.t * mDimensions), mDimensions - 1); + return sampleAt(getImageForFace(addr.face).getPixelRef(x, y)); +} + +inline Cubemap::Texel Cubemap::filterAt(const filament::math::float3& direction) const { + Cubemap::Address addr(getAddressFor(direction)); + addr.s = std::min(addr.s * mDimensions, mUpperBound); + addr.t = std::min(addr.t * mDimensions, mUpperBound); + return filterAt(getImageForFace(addr.face), addr.s, addr.t); +} + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAP_H */ diff --git a/ios/include/ibl/CubemapIBL.h b/ios/include/ibl/CubemapIBL.h new file mode 100644 index 00000000..925e27c0 --- /dev/null +++ b/ios/include/ibl/CubemapIBL.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAPIBL_H +#define IBL_CUBEMAPIBL_H + +#include + +#include +#include + +#include + +#include +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class Cubemap; +class Image; + +/** + * Generates cubemaps for the IBL. + */ +class UTILS_PUBLIC CubemapIBL { +public: + typedef void (*Progress)(size_t, float, void*); + + /** + * Computes a roughness LOD using prefiltered importance sampling GGX + * + * @param dst the destination cubemap + * @param levels a list of prefiltered lods of the source environment + * @param linearRoughness roughness + * @param maxNumSamples number of samples for importance sampling + * @param updater a callback for the caller to track progress + */ + static void roughnessFilter( + utils::JobSystem& js, Cubemap& dst, const utils::Slice& levels, + float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, + Progress updater = nullptr, void* userdata = nullptr); + + static void roughnessFilter( + utils::JobSystem& js, Cubemap& dst, const std::vector& levels, + float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, + Progress updater = nullptr, void* userdata = nullptr); + + //! Computes the "DFG" term of the "split-sum" approximation and stores it in a 2D image + static void DFG(utils::JobSystem& js, Image& dst, bool multiscatter, bool cloth); + + /** + * Computes the diffuse irradiance using prefiltered importance sampling GGX + * + * @note Usually this is done using spherical harmonics instead. + * + * @param dst the destination cubemap + * @param levels a list of prefiltered lods of the source environment + * @param maxNumSamples number of samples for importance sampling + * @param updater a callback for the caller to track progress + * + * @see CubemapSH + */ + static void diffuseIrradiance(utils::JobSystem& js, Cubemap& dst, const std::vector& levels, + size_t maxNumSamples = 1024, Progress updater = nullptr, void* userdata = nullptr); + + // for debugging. ignore. + static void brdf(utils::JobSystem& js, Cubemap& dst, float linearRoughness); +}; + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAPIBL_H */ diff --git a/ios/include/ibl/CubemapSH.h b/ios/include/ibl/CubemapSH.h new file mode 100644 index 00000000..b9297c74 --- /dev/null +++ b/ios/include/ibl/CubemapSH.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAPSH_H +#define IBL_CUBEMAPSH_H + + +#include + +#include +#include + +#include +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class Cubemap; + +/** + * Computes spherical harmonics + */ +class UTILS_PUBLIC CubemapSH { +public: + /** + * Spherical Harmonics decomposition of the given cubemap + * Optionally calculates irradiance by convolving with truncated cos. + */ + static std::unique_ptr computeSH( + utils::JobSystem& js, const Cubemap& cm, size_t numBands, bool irradiance); + + /** + * Render given spherical harmonics into a cubemap + */ + static void renderSH(utils::JobSystem& js, Cubemap& cm, + const std::unique_ptr& sh, size_t numBands); + + static void windowSH(std::unique_ptr& sh, size_t numBands, float cutoff); + + /** + * Compute spherical harmonics of the irradiance of the given cubemap. + * The SH basis are pre-scaled for easier rendering by the shader. The resulting coefficients + * are not spherical harmonics (as they're scalled by various factors). In particular they + * cannot be rendered with renderSH() above. Instead use renderPreScaledSH3Bands() which + * is exactly the code ran by our shader. + */ + static void preprocessSHForShader(std::unique_ptr& sh); + + /** + * Render pre-scaled irrandiance SH + */ + static void renderPreScaledSH3Bands(utils::JobSystem& js, Cubemap& cm, + const std::unique_ptr& sh); + + static constexpr size_t getShIndex(ssize_t m, size_t l) { + return SHindex(m, l); + } + +private: + class float5 { + float v[5]; + public: + float5() = default; + constexpr float5(float a, float b, float c, float d, float e) : v{ a, b, c, d, e } {} + constexpr float operator[](size_t i) const { return v[i]; } + float& operator[](size_t i) { return v[i]; } + }; + + static inline const float5 multiply(const float5 M[5], float5 x) noexcept { + return float5{ + M[0][0] * x[0] + M[1][0] * x[1] + M[2][0] * x[2] + M[3][0] * x[3] + M[4][0] * x[4], + M[0][1] * x[0] + M[1][1] * x[1] + M[2][1] * x[2] + M[3][1] * x[3] + M[4][1] * x[4], + M[0][2] * x[0] + M[1][2] * x[1] + M[2][2] * x[2] + M[3][2] * x[3] + M[4][2] * x[4], + M[0][3] * x[0] + M[1][3] * x[1] + M[2][3] * x[2] + M[3][3] * x[3] + M[4][3] * x[4], + M[0][4] * x[0] + M[1][4] * x[1] + M[2][4] * x[2] + M[3][4] * x[3] + M[4][4] * x[4] + }; + }; + + + static inline constexpr size_t SHindex(ssize_t m, size_t l) { + return l * (l + 1) + m; + } + + static void computeShBasis(float* SHb, size_t numBands, const math::float3& s); + + static float Kml(ssize_t m, size_t l); + + static std::vector Ki(size_t numBands); + + static constexpr float computeTruncatedCosSh(size_t l); + + static float sincWindow(size_t l, float w); + + static math::float3 rotateShericalHarmonicBand1(math::float3 band1, math::mat3f const& M); + + static float5 rotateShericalHarmonicBand2(float5 const& band2, math::mat3f const& M); + + // debugging only... + static float Legendre(ssize_t l, ssize_t m, float x); + static float TSH(int l, int m, const math::float3& d); + static void printShBase(std::ostream& out, int l, int m); +}; + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAPSH_H */ diff --git a/ios/include/ibl/CubemapUtils.h b/ios/include/ibl/CubemapUtils.h new file mode 100644 index 00000000..3b4a3154 --- /dev/null +++ b/ios/include/ibl/CubemapUtils.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAP_UTILS_H +#define IBL_CUBEMAP_UTILS_H + +#include +#include + +#include + +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class CubemapIBL; + +/** + * Create and convert Cubemap formats + */ +class UTILS_PUBLIC CubemapUtils { +public: + //! Creates a cubemap object and its backing Image + static Cubemap create(Image& image, size_t dim, bool horizontal = true); + + struct EmptyState { + }; + + template + using ScanlineProc = std::function< + void(STATE& state, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t width)>; + + template + using ReduceProc = std::function; + + //! process the cubemap using multithreading + template + static void process(Cubemap& cm, + utils::JobSystem& js, + ScanlineProc proc, + ReduceProc reduce = [](STATE&) {}, + const STATE& prototype = STATE()); + + //! process the cubemap + template + static void processSingleThreaded(Cubemap& cm, + utils::JobSystem& js, + ScanlineProc proc, + ReduceProc reduce = [](STATE&) {}, + const STATE& prototype = STATE()); + + //! clamps image to acceptable range + static void clamp(Image& src); + + static void highlight(Image& src); + + //! Downsamples a cubemap by helf in x and y using a box filter + static void downsampleCubemapLevelBoxFilter(utils::JobSystem& js, Cubemap& dst, const Cubemap& src); + + //! Return the name of a face (suitable for a file name) + static const char* getFaceName(Cubemap::Face face); + + //! computes the solid angle of a pixel of a face of a cubemap + static float solidAngle(size_t dim, size_t u, size_t v); + + //! Sets a Cubemap faces from a cross image + static void setAllFacesFromCross(Cubemap& cm, const Image& image); + +private: + + //move these into cmgen? + static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image); + static Image createCubemapImage(size_t dim, bool horizontal = true); + +#ifndef FILAMENT_IBL_LITE + +public: + + //! Converts horizontal or vertical cross Image to a Cubemap + static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src); + + //! Converts equirectangular Image to a Cubemap + static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src); + + //! Converts a Cubemap to an equirectangular Image + static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src); + + //! Converts a Cubemap to an octahedron + static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src); + + //! mirror the cubemap in the horizontal direction + static void mirrorCubemap(utils::JobSystem& js, Cubemap& dst, const Cubemap& src); + + //! generates a UV grid in the cubemap -- useful for debugging. + static void generateUVGrid(utils::JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY); + +#endif + + friend class CubemapIBL; +}; + + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAP_UTILS_H */ diff --git a/ios/include/ibl/Image.h b/ios/include/ibl/Image.h new file mode 100644 index 00000000..1ebaa62b --- /dev/null +++ b/ios/include/ibl/Image.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_IMAGE_H +#define IBL_IMAGE_H + +#include +#include +#include + +#include + +#include + +namespace filament { +namespace ibl { + +class UTILS_PUBLIC Image { +public: + Image(); + Image(size_t w, size_t h, size_t stride = 0); + + void reset(); + + void set(Image const& image); + + void subset(Image const& image, size_t x, size_t y, size_t w, size_t h); + + bool isValid() const { return mData != nullptr; } + + size_t getWidth() const { return mWidth; } + + size_t getStride() const { return mBpr / getBytesPerPixel(); } + + size_t getHeight() const { return mHeight; } + + size_t getBytesPerRow() const { return mBpr; } + + size_t getBytesPerPixel() const { return sizeof(math::float3); } + + void* getData() const { return mData; } + + size_t getSize() const { return mBpr * mHeight; } + + void* getPixelRef(size_t x, size_t y) const; + + std::unique_ptr detach() { return std::move(mOwnedData); } + +private: + size_t mBpr = 0; + size_t mWidth = 0; + size_t mHeight = 0; + std::unique_ptr mOwnedData; + void* mData = nullptr; +}; + +inline void* Image::getPixelRef(size_t x, size_t y) const { + return static_cast(mData) + y * getBytesPerRow() + x * getBytesPerPixel(); +} + +} // namespace ibl +} // namespace filament + +#endif /* IBL_IMAGE_H */ diff --git a/ios/include/ibl/utilities.h b/ios/include/ibl/utilities.h new file mode 100644 index 00000000..6d40cc03 --- /dev/null +++ b/ios/include/ibl/utilities.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_UTILITIES_H +#define IBL_UTILITIES_H + +#include + +#include +#include + +namespace filament { +namespace ibl { + +template +static inline constexpr T sq(T x) { + return x * x; +} + +template +static inline constexpr T log4(T x) { + // log2(x)/log2(4) + // log2(x)/2 + return std::log2(x) * T(0.5); +} + +inline bool isPOT(size_t x) { + return !(x & (x - 1)); +} + +inline filament::math::float2 hammersley(uint32_t i, float iN) { + constexpr float tof = 0.5f / 0x80000000U; + uint32_t bits = i; + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return { i * iN, bits * tof }; +} + +} // namespace ibl +} // namespace filament +#endif /* IBL_UTILITIES_H */ diff --git a/ios/include/imageio/BlockCompression.h b/ios/include/imageio/BlockCompression.h deleted file mode 100644 index 1b742590..00000000 --- a/ios/include/imageio/BlockCompression.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! \file Functions and types related to block-compressed texture formats. - -#ifndef IMAGEIO_BLOCKCOMPRESSION_H_ -#define IMAGEIO_BLOCKCOMPRESSION_H_ - -#include - -#include -#include - -#include - -#include - -#include - -namespace image { - -enum class CompressedFormat { - INVALID = 0, - - R11_EAC = 0x9270, - SIGNED_R11_EAC = 0x9271, - RG11_EAC = 0x9272, - SIGNED_RG11_EAC = 0x9273, - RGB8_ETC2 = 0x9274, - SRGB8_ETC2 = 0x9275, - RGB8_ALPHA1_ETC2 = 0x9276, - SRGB8_ALPHA1_ETC = 0x9277, - RGBA8_ETC2_EAC = 0x9278, - SRGB8_ALPHA8_ETC2_EAC = 0x9279, - - RGB_S3TC_DXT1 = 0x83F0, - RGBA_S3TC_DXT1 = 0x83F1, - RGBA_S3TC_DXT3 = 0x83F2, - RGBA_S3TC_DXT5 = 0x83F3, - SRGB_S3TC_DXT1 = 0x8C4C, - SRGB_ALPHA_S3TC_DXT1 = 0x8C4D, - SRGB_ALPHA_S3TC_DXT3 = 0x8C4E, - SRGB_ALPHA_S3TC_DXT5 = 0x8C4F, - - RGBA_ASTC_4x4 = 0x93B0, - RGBA_ASTC_5x4 = 0x93B1, - RGBA_ASTC_5x5 = 0x93B2, - RGBA_ASTC_6x5 = 0x93B3, - RGBA_ASTC_6x6 = 0x93B4, - RGBA_ASTC_8x5 = 0x93B5, - RGBA_ASTC_8x6 = 0x93B6, - RGBA_ASTC_8x8 = 0x93B7, - RGBA_ASTC_10x5 = 0x93B8, - RGBA_ASTC_10x6 = 0x93B9, - RGBA_ASTC_10x8 = 0x93BA, - RGBA_ASTC_10x10 = 0x93BB, - RGBA_ASTC_12x10 = 0x93BC, - RGBA_ASTC_12x12 = 0x93BD, - SRGB8_ALPHA8_ASTC_4x4 = 0x93D0, - SRGB8_ALPHA8_ASTC_5x4 = 0x93D1, - SRGB8_ALPHA8_ASTC_5x5 = 0x93D2, - SRGB8_ALPHA8_ASTC_6x5 = 0x93D3, - SRGB8_ALPHA8_ASTC_6x6 = 0x93D4, - SRGB8_ALPHA8_ASTC_8x5 = 0x93D5, - SRGB8_ALPHA8_ASTC_8x6 = 0x93D6, - SRGB8_ALPHA8_ASTC_8x8 = 0x93D7, - SRGB8_ALPHA8_ASTC_10x5 = 0x93D8, - SRGB8_ALPHA8_ASTC_10x6 = 0x93D9, - SRGB8_ALPHA8_ASTC_10x8 = 0x93DA, - SRGB8_ALPHA8_ASTC_10x10 = 0x93DB, - SRGB8_ALPHA8_ASTC_12x10 = 0x93DC, - SRGB8_ALPHA8_ASTC_12x12 = 0x93DD, -}; - -// Represents the opaque result of compression and the chosen texture format. -struct CompressedTexture { - const CompressedFormat format; - const uint32_t size; - std::unique_ptr data; -}; - -// ASTC //////////////////////////////////////////////////////////////////////////////////////////// - -// Controls how fast compression occurs at the cost of quality in the resulting image. -enum class AstcPreset { - VERYFAST, - FAST, - MEDIUM, - THOROUGH, - EXHAUSTIVE, -}; - -// Informs the encoder what texels represent; this is especially crucial for normal maps. -enum class AstcSemantic { - COLORS_LDR, - COLORS_HDR, - NORMALS, -}; - -// The encoder configuration controls the quality and speed of compression, as well as the resulting -// format. The specified block size must be one of the 14 block sizes that can be consumed by ES 3.2 -// as per https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glCompressedTexImage2D.xhtml -struct AstcConfig { - AstcPreset quality; - AstcSemantic semantic; - filament::math::ushort2 blocksize; - bool srgb; -}; - -// Uses the CPU to compress a linear image (1 to 4 channels) into an ASTC texture. The 16-byte -// header block that ARM uses in their file format is not included. -CompressedTexture astcCompress(const LinearImage& source, AstcConfig config); - -// Parses a simple underscore-delimited string to produce an ASTC compression configuration. This -// makes it easy to incorporate the compression API into command-line tools. If the string is -// malformed, this returns a config with a 0x0 blocksize. Example strings: fast_ldr_4x4, -// thorough_normals_6x6, veryfast_hdr_12x10 -AstcConfig astcParseOptionString(const std::string& options); - -// ETC ///////////////////////////////////////////////////////////////////////////////////////////// - -enum class EtcErrorMetric { - RGBA, - RGBX, - REC709, - NUMERIC, - NORMALXYZ, -}; - -// Informs the ETC encoder of the desired output. Effort sets the quality / speed tradeoff with -// a number between 0 and 100. -struct EtcConfig { - CompressedFormat format; - EtcErrorMetric metric; - int effort; -}; - -// Uses the CPU to compress a linear image (1 to 4 channels) into an ETC texture. -CompressedTexture etcCompress(const LinearImage& source, EtcConfig config); - -// Converts a string into an ETC compression configuration where the string has the form -// FORMAT_METRIC_EFFORT where: -// - FORMAT is one of: r11, signed_r11, rg11, signed_rg11, rgb8, srgb8, rgb8_alpha, -// srgb8_alpha, rgba8, and srgb8_alpha8 -// - METRIC is one of: rgba, rgbx, rec709, numeric, and normalxyz -// - EFFORT is an integer between 0 and 100 -EtcConfig etcParseOptionString(const std::string& options); - -// S3TC //////////////////////////////////////////////////////////////////////////////////////////// - -// Informs the S3TC encoder of the desired output. -struct S3tcConfig { - CompressedFormat format; - bool srgb; -}; - -// Uses the CPU to compress a linear image (1 to 4 channels) into an S3TC texture. -CompressedTexture s3tcCompress(const LinearImage& source, S3tcConfig config); - -// Parses an underscore-delimited string to produce an S3TC compression configuration. Currently -// this only accepts "rgb_dxt1" and "rgba_dxt5". If the string is malformed, this returns a config -// with an invalid format. -S3tcConfig s3tcParseOptionString(const std::string& options); - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -struct CompressionConfig { - enum { INVALID, ASTC, S3TC, ETC } type; - AstcConfig astc; - S3tcConfig s3tc; - EtcConfig etc; -}; - -bool parseOptionString(const std::string& options, CompressionConfig* config); - -UTILS_PUBLIC -CompressedTexture compressTexture(const CompressionConfig& config, const LinearImage& image); - -} // namespace image - -#endif /* IMAGEIO_BLOCKCOMPRESSION_H_ */ diff --git a/ios/include/imageio/HDRDecoder.h b/ios/include/imageio/HDRDecoder.h deleted file mode 100644 index bb7519ee..00000000 --- a/ios/include/imageio/HDRDecoder.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IMAGE_HDRDECODER_H_ -#define IMAGE_HDRDECODER_H_ - -#include - -namespace image { - -class HDRDecoder : public ImageDecoder::Decoder { -public: - static HDRDecoder* create(std::istream& stream); - static bool checkSignature(char const* buf); - - HDRDecoder(const HDRDecoder&) = delete; - HDRDecoder& operator=(const HDRDecoder&) = delete; - -private: - explicit HDRDecoder(std::istream& stream); - ~HDRDecoder() override; - - // ImageDecoder::Decoder interface - LinearImage decode() override; - - static const char sigRadiance[]; - static const char sigRGBE[]; - std::istream& mStream; - std::streampos mStreamStartPos; -}; - -} // namespace image - -#endif /* IMAGE_IMAGEDECODER_H_ */ diff --git a/ios/include/imageio/ImageDecoder.h b/ios/include/imageio/ImageDecoder.h deleted file mode 100644 index cd341c49..00000000 --- a/ios/include/imageio/ImageDecoder.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IMAGE_IMAGEDECODER_H_ -#define IMAGE_IMAGEDECODER_H_ - -#include -#include - -#include - -#include - -namespace image { - -class UTILS_PUBLIC ImageDecoder { -public: - enum class ColorSpace { - LINEAR, - SRGB - }; - - // Returns linear floating-point data, or a non-valid image if an error occured. - static LinearImage decode(std::istream& stream, const std::string& sourceName, - ColorSpace sourceSpace = ColorSpace::SRGB); - - class Decoder { - public: - virtual LinearImage decode() = 0; - virtual ~Decoder() = default; - - ColorSpace getColorSpace() const noexcept { - return mColorSpace; - } - - void setColorSpace(ColorSpace colorSpace) noexcept { - mColorSpace = colorSpace; - } - - private: - ColorSpace mColorSpace = ColorSpace::SRGB; - }; - -private: - enum class Format { - NONE, - PNG, - HDR, - PSD, - EXR - }; -}; - -} // namespace image - -#endif /* IMAGE_IMAGEDECODER_H_ */ diff --git a/ios/include/imageio/ImageDiffer.h b/ios/include/imageio/ImageDiffer.h deleted file mode 100644 index c3c752e3..00000000 --- a/ios/include/imageio/ImageDiffer.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -namespace image { - -enum class ComparisonMode { - SKIP, - COMPARE, - UPDATE, -}; - -// Saves an image to disk or does a load-and-compare, depending on comparison mode. -// This makes it easy for unit tests to have compare / update commands. -// The passed-in image is the "result image" and the expected image is the "golden image". -void updateOrCompare(LinearImage result, const utils::Path& golden, ComparisonMode, float epsilon); - -} // namespace image diff --git a/ios/include/imageio/ImageEncoder.h b/ios/include/imageio/ImageEncoder.h deleted file mode 100644 index 7cd4bd8f..00000000 --- a/ios/include/imageio/ImageEncoder.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IMAGE_IMAGEENCODER_H_ -#define IMAGE_IMAGEENCODER_H_ - -#include -#include - -#include - -#include - -namespace image { - -class UTILS_PUBLIC ImageEncoder { -public: - enum class Format { - PNG, // 8-bit sRGB, 1 or 3 channels - PNG_LINEAR, // 8-bit linear RGB, 1 or 3 channels - HDR, // 8-bit linear RGBE, 3 channels only - RGBM, // 8-bit RGBM, as PNG, 3 channels only - PSD, // 16-bit sRGB or 32-bit linear RGB, 3 channels only - // Default: 16 bit - EXR, // 16-bit linear RGB (half-float), 3 channels only - // Default: PIZ compression - DDS, // 8-bit sRGB, 1, 2 or 3 channels; - // 16-bit or 32-bit linear RGB, 1, 2 or 3 channels - // Default: 16 bit - DDS_LINEAR, // 8-bit, 16-bit or 32-bit linear RGB, 1, 2 or 3 channels - // Default: 16 bit - RGB_10_11_11_REV, // RGBA PNG file, but containing 11_11_10 data - }; - - // Consumes linear floating-point data, returns false if unable to encode. - static bool encode(std::ostream& stream, Format format, const LinearImage& image, - const std::string& compression, const std::string& destName); - - static Format chooseFormat(const std::string& name, bool forceLinear = false); - static std::string chooseExtension(Format format); - - class Encoder { - public: - virtual bool encode(const LinearImage& image) = 0; - virtual ~Encoder() = default; - }; -}; - -} // namespace image - -#endif /* IMAGE_IMAGEENCODER_H_ */ diff --git a/ios/include/math/TMatHelpers.h b/ios/include/math/TMatHelpers.h index 818bb9fa..ec66650c 100644 --- a/ios/include/math/TMatHelpers.h +++ b/ios/include/math/TMatHelpers.h @@ -21,7 +21,7 @@ #include #include -#include // for std::swap +#include // for std::swap and std::min #include // for std:: namespace #include diff --git a/ios/include/math/TVecHelpers.h b/ios/include/math/TVecHelpers.h index f213dc71..a43c2a47 100644 --- a/ios/include/math/TVecHelpers.h +++ b/ios/include/math/TVecHelpers.h @@ -21,14 +21,10 @@ #include // for std:: namespace -#include #include #include -namespace filament { -namespace math { -namespace details { -// ------------------------------------------------------------------------------------- +namespace filament::math::details { template inline constexpr U min(U a, U b) noexcept { @@ -281,8 +277,6 @@ private: template friend inline constexpr bool MATH_PURE operator==(const VECTOR& lv, const VECTOR& rv) { - // w/ inlining we end-up with many branches that will pollute the BPU cache - MATH_NOUNROLL for (size_t i = 0; i < lv.size(); i++) { if (lv[i] != rv[i]) { return false; @@ -624,9 +618,6 @@ private: } }; -// ------------------------------------------------------------------------------------- -} // namespace details -} // namespace math -} // namespace filament +} // namespace filament::math::details #endif // TNT_MATH_TVECHELPERS_H diff --git a/ios/include/math/quat.h b/ios/include/math/quat.h index be00f1ec..91e1ab6d 100644 --- a/ios/include/math/quat.h +++ b/ios/include/math/quat.h @@ -126,6 +126,44 @@ public: constexpr static TQuaternion MATH_PURE fromAxisAngle(const TVec3& axis, B angle) { return TQuaternion(std::sin(angle * 0.5) * normalize(axis), std::cos(angle * 0.5)); } + + // constructs a quaternion from orig to dest. + // it returns the shortest arc and `from` and `to` must be normalized. + template> + constexpr static TQuaternion MATH_PURE fromDirectedRotation(const TVec3& from, const TVec3& to) { + // see the implementation of glm/gtx/quaternion.hpp + T cosTheta = dot(from, to); + TVec3 rotationAxis; + + if (cosTheta >= T(1) - std::numeric_limits::epsilon()) { + // orig and dest point in the same direction + return TQuaternion(1, 0, 0, 0); + } + + if (cosTheta < T(-1) + std::numeric_limits::epsilon()) { + // special case when vectors in opposite directions : + // there is no "ideal" rotation axis + // So guess one; any will do as long as it's perpendicular to start + // This implementation favors a rotation around the Up axis (Y), + // since it's often what you want to do. + rotationAxis = cross(TVec3(0, 0, 1), from); + + if (length2(rotationAxis) < std::numeric_limits::epsilon()) { + // bad luck, they were parallel, try again! + rotationAxis = cross(TVec3(1, 0, 0), from); + } + + rotationAxis = normalize(rotationAxis); + return fromAxisAngle(rotationAxis, F_PI); + } + + // implementation from Stan Melax's Game Programming Gems 1 article + rotationAxis = cross(from, to); + + const T s = std::sqrt((T(1) + cosTheta) * T(2)); + return TQuaternion(s * T(0.5), + rotationAxis.x / s, rotationAxis.y / s, rotationAxis.z / s); + } }; } // namespace details diff --git a/ios/include/mathio/ostream.h b/ios/include/mathio/ostream.h new file mode 100644 index 00000000..5f29c879 --- /dev/null +++ b/ios/include/mathio/ostream.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#if __has_attribute(visibility) +# define MATHIO_PUBLIC __attribute__((visibility("default"))) +#else +# define MATHIO_PUBLIC +#endif + +namespace filament { +namespace math { + +namespace details { template class TQuaternion; } + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec2& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec3& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec4& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat22& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat33& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat44& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TQuaternion& v) noexcept; + +} // namespace math +} // namespace filament diff --git a/ios/include/src/Bookmark.cpp b/ios/include/src/Bookmark.cpp deleted file mode 100644 index 9c16177b..00000000 --- a/ios/include/src/Bookmark.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -using namespace filament::math; - -namespace filament { -namespace camutils { - -template -Bookmark Bookmark::interpolate(Bookmark a, Bookmark b, double t) { - Bookmark result; - using float3 = filament::math::vec3; - - if (a.mode == Mode::MAP) { - assert(b.mode == Mode::MAP); - const double rho = sqrt(2.0); - const double rho2 = 2, rho4 = 4; - const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent; - const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent; - const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2); - const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1); - const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1); - const double r0 = log(sqrt(b0 * b0 + 1.0) - b0); - const double r1 = log(sqrt(b1 * b1 + 1) - b1); - const double dr = r1 - r0; - const int valid = !std::isnan(dr) && dr != 0; - const double S = (valid ? dr : log(w1 / w0)) / rho; - const double s = t * S; - - // This performs Van Wijk interpolation to animate between two waypoints on a map. - if (valid) { - const double coshr0 = cosh(r0); - const double u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); - Bookmark result; - result.map.center.x = ux0 + u * dx; - result.map.center.y = uy0 + u * dy; - result.map.extent = w0 * coshr0 / cosh(rho * s + r0); - return result; - } - - // For degenerate cases, fall back to a simplified interpolation method. - result.map.center.x = ux0 + t * dx; - result.map.center.y = uy0 + t * dy; - result.map.extent = w0 * exp(rho * s); - return result; -} - - assert(b.mode == Mode::ORBIT); - result.orbit.phi = lerp(a.orbit.phi, b.orbit.phi, FLOAT(t)); - result.orbit.theta = lerp(a.orbit.theta, b.orbit.theta, FLOAT(t)); - result.orbit.distance = lerp(a.orbit.distance, b.orbit.distance, FLOAT(t)); - result.orbit.pivot = lerp(a.orbit.pivot, b.orbit.pivot, float3(t)); - return result; -} - -// Uses the Van Wijk method to suggest a duration for animating between two waypoints on a map. -// This does not have units, so just use it as a multiplier. -template -double Bookmark::duration(Bookmark a, Bookmark b) { - assert(a.mode == Mode::ORBIT && b.mode == Mode::ORBIT); - const double rho = sqrt(2.0); - const double rho2 = 2, rho4 = 4; - const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent; - const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent; - const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2); - const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1); - const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1); - const double r0 = log(sqrt(b0 * b0 + 1.0) - b0); - const double r1 = log(sqrt(b1 * b1 + 1) - b1); - const double dr = r1 - r0; - const int valid = !std::isnan(dr) && dr != 0; - const double S = (valid ? dr : log(w1 / w0)) / rho; - return fabs(S); -} - -template class Bookmark; - -} // namespace camutils -} // namespace filament diff --git a/ios/include/src/FreeFlightManipulator.h b/ios/include/src/FreeFlightManipulator.h deleted file mode 100644 index 07f5b215..00000000 --- a/ios/include/src/FreeFlightManipulator.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CAMUTILS_FREEFLIGHT_MANIPULATOR_H -#define CAMUTILS_FREEFLIGHT_MANIPULATOR_H - -#include - -#include -#include -#include -#include - -#include - -namespace filament { -namespace camutils { - -using namespace filament::math; - -template -class FreeFlightManipulator : public Manipulator { -public: - using vec2 = filament::math::vec2; - using vec3 = filament::math::vec3; - using vec4 = filament::math::vec4; - using Bookmark = filament::camutils::Bookmark; - using Base = Manipulator; - using Config = typename Base::Config; - - FreeFlightManipulator(Mode mode, const Config& props) : Base(mode, props) { - setProperties(props); - Base::mEye = Base::mProps.flightStartPosition; - const auto pitch = Base::mProps.flightStartPitch; - const auto yaw = Base::mProps.flightStartYaw; - mTargetEuler = {pitch, yaw}; - updateTarget(pitch, yaw); - } - - void setProperties(const Config& props) override { - Config resolved = props; - - if (resolved.flightPanSpeed == vec2(0, 0)) { - resolved.flightPanSpeed = vec2(0.01, 0.01); - } - if (resolved.flightMaxSpeed == 0.0) { - resolved.flightMaxSpeed = 10.0; - } - if (resolved.flightSpeedSteps == 0) { - resolved.flightSpeedSteps = 80; - } - - Base::setProperties(resolved); - } - - void updateTarget(FLOAT pitch, FLOAT yaw) { - Base::mTarget = Base::mEye + (mat3::eulerZYX(0, yaw, pitch) * vec3(0.0, 0.0, -1.0)); - } - - void grabBegin(int x, int y, bool strafe) override { - mGrabWin = {x, y}; - mGrabbing = true; - mGrabEuler = mTargetEuler; - } - - void grabUpdate(int x, int y) override { - if (!mGrabbing) { - return; - } - - const vec2 del = mGrabWin - vec2{x, y}; - - const auto& grabPitch = mGrabEuler.x; - const auto& grabYaw = mGrabEuler.y; - auto& pitch = mTargetEuler.x; - auto& yaw = mTargetEuler.y; - - constexpr double EPSILON = 0.001; - - auto panSpeed = Base::mProps.flightPanSpeed; - constexpr FLOAT minPitch = (-F_PI_2 + EPSILON); - constexpr FLOAT maxPitch = ( F_PI_2 - EPSILON); - pitch = clamp(grabPitch + del.y * -panSpeed.y, minPitch, maxPitch); - yaw = fmod(grabYaw + del.x * panSpeed.x, 2.0 * F_PI); - - updateTarget(pitch, yaw); - } - - void grabEnd() override { - mGrabbing = false; - } - - void keyDown(typename Base::Key key) override { - mKeyDown[(int) key] = true; - } - - void keyUp(typename Base::Key key) override { - mKeyDown[(int) key] = false; - } - - void scroll(int x, int y, FLOAT scrolldelta) override { - const FLOAT halfSpeedSteps = Base::mProps.flightSpeedSteps / 2; - mScrollWheel = clamp(mScrollWheel + scrolldelta, -halfSpeedSteps, halfSpeedSteps); - // Normalize the scroll position from -1 to 1 and calculate the move speed, in world - // units per second. - mScrollPositionNormalized = (mScrollWheel + halfSpeedSteps) / halfSpeedSteps - 1.0; - mMoveSpeed = pow(Base::mProps.flightMaxSpeed, mScrollPositionNormalized); - } - - void update(FLOAT deltaTime) override { - vec3 forceLocal { 0.0, 0.0, 0.0 }; - - if (mKeyDown[(int) Base::Key::FORWARD]) { - forceLocal += vec3{ 0.0, 0.0, -1.0 }; - } - if (mKeyDown[(int) Base::Key::LEFT]) { - forceLocal += vec3{ -1.0, 0.0, 0.0 }; - } - if (mKeyDown[(int) Base::Key::BACKWARD]) { - forceLocal += vec3{ 0.0, 0.0, 1.0 }; - } - if (mKeyDown[(int) Base::Key::RIGHT]) { - forceLocal += vec3{ 1.0, 0.0, 0.0 }; - } - - const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector); - vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz; - - if (mKeyDown[(int) Base::Key::UP]) { - forceWorld += vec3{ 0.0, 1.0, 0.0 }; - } - if (mKeyDown[(int) Base::Key::DOWN]) { - forceWorld += vec3{ 0.0, -1.0, 0.0 }; - } - - forceWorld *= mMoveSpeed; - - const auto dampingFactor = Base::mProps.flightMoveDamping; - if (dampingFactor == 0.0) { - // Without damping, we simply treat the force as our velocity. - mEyeVelocity = forceWorld; - } else { - // The dampingFactor acts as "friction", which acts upon the camera in the direction - // opposite its velocity. - // Force is also multiplied by the dampingFactor, to "make up" for the friction. - // This ensures that the max velocity still approaches mMoveSpeed; - vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor; - mEyeVelocity += velocityDelta * deltaTime; - } - - const vec3 positionDelta = mEyeVelocity * deltaTime; - - Base::mEye += positionDelta; - Base::mTarget += positionDelta; - } - - Bookmark getCurrentBookmark() const override { - Bookmark bookmark; - bookmark.flight.position = Base::mEye; - bookmark.flight.pitch = mTargetEuler.x; - bookmark.flight.yaw = mTargetEuler.y; - return bookmark; - } - - Bookmark getHomeBookmark() const override { - Bookmark bookmark; - bookmark.flight.position = Base::mProps.flightStartPosition;; - bookmark.flight.pitch = Base::mProps.flightStartPitch; - bookmark.flight.yaw = Base::mProps.flightStartYaw; - return bookmark; - } - - void jumpToBookmark(const Bookmark& bookmark) override { - Base::mEye = bookmark.flight.position; - updateTarget(bookmark.flight.pitch, bookmark.flight.yaw); - } - -private: - vec2 mGrabWin; - vec2 mTargetEuler; // (pitch, yaw) - vec2 mGrabEuler; // (pitch, yaw) - bool mKeyDown[(int) Base::Key::COUNT] = {false}; - bool mGrabbing = false; - FLOAT mScrollWheel = 0.0f; - FLOAT mScrollPositionNormalized = 0.0f; - FLOAT mMoveSpeed = 1.0f; - vec3 mEyeVelocity; -}; - -} // namespace camutils -} // namespace filament - -#endif /* CAMUTILS_FREEFLIGHT_MANIPULATOR_H */ diff --git a/ios/include/src/Manipulator.cpp b/ios/include/src/Manipulator.cpp deleted file mode 100644 index d44528cd..00000000 --- a/ios/include/src/Manipulator.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include "FreeFlightManipulator.h" -#include "MapManipulator.h" -#include "OrbitManipulator.h" - -using namespace filament::math; - -namespace filament { -namespace camutils { - -template typename -Manipulator::Builder& Manipulator::Builder::viewport(int width, int height) { - details.viewport[0] = width; - details.viewport[1] = height; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) { - details.targetPosition = {x, y, z}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) { - details.upVector = {x, y, z}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::zoomSpeed(FLOAT val) { - details.zoomSpeed = val; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) { - details.orbitHomePosition = {x, y, z}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::orbitSpeed(FLOAT x, FLOAT y) { - details.orbitSpeed = {x, y}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::fovDirection(Fov fov) { - details.fovDirection = fov; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::fovDegrees(FLOAT degrees) { - details.fovDegrees = degrees; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::farPlane(FLOAT distance) { - details.farPlane = distance; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) { - details.mapExtent = {worldWidth, worldHeight}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::mapMinDistance(FLOAT mindist) { - details.mapMinDistance = mindist; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) { - details.flightStartPosition = {x, y, z}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) { - details.flightStartPitch = pitch; - details.flightStartYaw = yaw; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) { - details.flightMaxSpeed = maxSpeed; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightSpeedSteps(int steps) { - details.flightSpeedSteps = steps; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightPanSpeed(FLOAT x, FLOAT y) { - details.flightPanSpeed = {x, y}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::flightMoveDamping(FLOAT damping) { - details.flightMoveDamping = damping; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) { - details.groundPlane = {a, b, c, d}; - return *this; -} - -template typename -Manipulator::Builder& Manipulator::Builder::raycastCallback(RayCallback cb, void* userdata) { - details.raycastCallback = cb; - details.raycastUserdata = userdata; - return *this; -} - -template -Manipulator* Manipulator::Builder::build(Mode mode) { - switch (mode) { - case Mode::FREE_FLIGHT: - return new FreeFlightManipulator(mode, details); - case Mode::MAP: - return new MapManipulator(mode, details); - case Mode::ORBIT: - return new OrbitManipulator(mode, details); - } -} - -template -Manipulator::Manipulator(Mode mode, const Config& props) : mMode(mode) { - setProperties(props); -} - -template -void Manipulator::setProperties(const Config& props) { - mProps = props; - - if (mProps.zoomSpeed == FLOAT(0)) { - mProps.zoomSpeed = 0.01; - } - - if (mProps.upVector == vec3(0)) { - mProps.upVector = vec3(0, 1, 0); - } - - if (mProps.fovDegrees == FLOAT(0)) { - mProps.fovDegrees = 33; - } - - if (mProps.farPlane == FLOAT(0)) { - mProps.farPlane = 5000; - } - - if (mProps.mapExtent == vec2(0)) { - mProps.mapExtent = vec2(512); - } -} - -template -void Manipulator::setViewport(int width, int height) { - Config props = mProps; - props.viewport[0] = width; - props.viewport[1] = height; - setProperties(props); -} - -template -void Manipulator::getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const { - *targetPosition = mTarget; - *eyePosition = mEye; - const vec3 gaze = normalize(mTarget - mEye); - const vec3 right = cross(gaze, mProps.upVector); - *upward = cross(right, gaze); -} - -template -static bool raycastPlane(const filament::math::vec3& origin, - const filament::math::vec3& dir, FLOAT* t, void* userdata) { - using vec3 = filament::math::vec3; - using vec4 = filament::math::vec4; - auto props = (const typename Manipulator::Config*) userdata; - const vec4 plane = props->groundPlane; - const vec3 n = vec3(plane[0], plane[1], plane[2]); - const vec3 p0 = n * plane[3]; - const FLOAT denom = -dot(n, dir); - if (denom > 1e-6) { - const vec3 p0l0 = p0 - origin; - *t = dot(p0l0, n) / -denom; - return *t >= 0; - } - return false; -} - -template -void Manipulator::getRay(int x, int y, vec3* porigin, vec3* pdir) const { - const vec3 gaze = normalize(mTarget - mEye); - const vec3 right = normalize(cross(gaze, mProps.upVector)); - const vec3 upward = cross(right, gaze); - const FLOAT width = mProps.viewport[0]; - const FLOAT height = mProps.viewport[1]; - const FLOAT fov = mProps.fovDegrees * F_PI / 180.0; - - // Remap the grid coordinate into [-1, +1] and shift it to the pixel center. - const FLOAT u = 2.0 * (0.5 + x) / width - 1.0; - const FLOAT v = 2.0 * (0.5 + y) / height - 1.0; - - // Compute the tangent of the field-of-view angle as well as the aspect ratio. - const FLOAT tangent = tan(fov / 2.0); - const FLOAT aspect = width / height; - - // Adjust the gaze so it goes through the pixel of interest rather than the grid center. - vec3 dir = gaze; - if (mProps.fovDirection == Fov::VERTICAL) { - dir += right * tangent * u * aspect; - dir += upward * tangent * v; - } else { - dir += right * tangent * u; - dir += upward * tangent * v / aspect; - } - dir = normalize(dir); - - *porigin = mEye; - *pdir = dir; -} - -template -bool Manipulator::raycast(int x, int y, vec3* result) const { - vec3 origin, dir; - getRay(x, y, &origin, &dir); - - // Choose either the user's callback function or the plane intersector. - auto callback = mProps.raycastCallback; - auto fallback = raycastPlane; - void* userdata = mProps.raycastUserdata; - if (!callback) { - callback = fallback; - userdata = (void*) &mProps; - } - - // If the ray misses, then try the fallback function. - FLOAT t; - if (!callback(mEye, dir, &t, userdata)) { - if (callback == fallback || !fallback(mEye, dir, &t, (void*) &mProps)) { - return false; - } - } - - *result = mEye + dir * t; - return true; -} - -template -filament::math::vec3 Manipulator::raycastFarPlane(int x, int y) const { - const filament::math::vec3 gaze = normalize(mTarget - mEye); - const vec3 right = cross(gaze, mProps.upVector); - const vec3 upward = cross(right, gaze); - const FLOAT width = mProps.viewport[0]; - const FLOAT height = mProps.viewport[1]; - const FLOAT fov = mProps.fovDegrees * math::F_PI / 180.0; - - // Remap the grid coordinate into [-1, +1] and shift it to the pixel center. - const FLOAT u = 2.0 * (0.5 + x) / width - 1.0; - const FLOAT v = 2.0 * (0.5 + y) / height - 1.0; - - // Compute the tangent of the field-of-view angle as well as the aspect ratio. - const FLOAT tangent = tan(fov / 2.0); - const FLOAT aspect = width / height; - - // Adjust the gaze so it goes through the pixel of interest rather than the grid center. - vec3 dir = gaze; - if (mProps.fovDirection == Fov::VERTICAL) { - dir += right * tangent * u * aspect; - dir += upward * tangent * v; - } else { - dir += right * tangent * u; - dir += upward * tangent * v / aspect; - } - return mEye + dir * mProps.farPlane; -} - -template -void Manipulator::keyDown(Manipulator::Key key) { } - -template -void Manipulator::keyUp(Manipulator::Key key) { } - -template -void Manipulator::update(FLOAT deltaTime) { } - -template class Manipulator; - -} // namespace camutils -} // namespace filament diff --git a/ios/include/src/MapManipulator.h b/ios/include/src/MapManipulator.h deleted file mode 100644 index 6df5b4c5..00000000 --- a/ios/include/src/MapManipulator.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CAMUTILS_MAP_MANIPULATOR_H -#define CAMUTILS_MAP_MANIPULATOR_H - -#include - -#include - -namespace filament { -namespace camutils { - -template -class MapManipulator : public Manipulator { -public: - using vec2 = math::vec2; - using vec3 = math::vec3; - using vec4 = math::vec4; - using Bookmark = filament::camutils::Bookmark; - using Base = Manipulator; - using Config = typename Manipulator::Config; - - MapManipulator(Mode mode, const Config& props) : Manipulator(mode, props) { - const FLOAT width = Base::mProps.mapExtent.x; - const FLOAT height = Base::mProps.mapExtent.y; - const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL; - const vec3 targetToEye = Base::mProps.groundPlane.xyz; - const FLOAT halfExtent = (horiz ? width : height) / 2.0; - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT distance = halfExtent / tan(fov / 2.0); - Base::mTarget = Base::mProps.targetPosition; - Base::mEye = Base::mTarget + distance * targetToEye; - } - - void grabBegin(int x, int y, bool strafe) override { - if (strafe || !Base::raycast(x, y, &mGrabScene)) { - return; - } - mGrabFar = Base::raycastFarPlane(x, y); - mGrabEye = Base::mEye; - mGrabTarget = Base::mTarget; - mGrabbing = true; - } - - void grabUpdate(int x, int y) override { - if (mGrabbing) { - const FLOAT ulen = distance(mGrabScene, mGrabEye); - const FLOAT vlen = distance(mGrabFar, mGrabScene); - const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen; - const vec3 eyePosition = mGrabEye + translation; - const vec3 targetPosition = mGrabTarget + translation; - moveWithConstraints(eyePosition, targetPosition); - } - } - - void grabEnd() override { - mGrabbing = false; - } - - void scroll(int x, int y, FLOAT scrolldelta) override { - vec3 grabScene; - if (!Base::raycast(x, y, &grabScene)) { - return; - } - - // Find the direction of travel for the dolly. We do not normalize since it - // is desirable to move faster when further away from the targetPosition. - vec3 u = grabScene - Base::mEye; - - // Prevent getting stuck when zooming in. - if (scrolldelta < 0) { - const FLOAT distanceToSurface = length(u); - if (distanceToSurface < Base::mProps.zoomSpeed) { - return; - } - } - - u *= -scrolldelta * Base::mProps.zoomSpeed; - - const vec3 eyePosition = Base::mEye + u; - const vec3 targetPosition = Base::mTarget + u; - moveWithConstraints(eyePosition, targetPosition); - } - - Bookmark getCurrentBookmark() const override { - const vec3 dir = normalize(Base::mTarget - Base::mEye); - - FLOAT distance; - raycastPlane(Base::mEye, dir, &distance); - - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT halfExtent = distance * tan(fov / 2.0); - - vec3 targetPosition = Base::mEye + dir * distance; - - const vec3 targetToEye = Base::mProps.groundPlane.xyz; - const vec3 uvec = cross(Base::mProps.upVector, targetToEye); - const vec3 vvec = cross(targetToEye, uvec); - const vec3 centerToTarget = targetPosition - Base::mProps.targetPosition; - - Bookmark bookmark; - bookmark.mode = Mode::MAP; - bookmark.map.extent = halfExtent * 2.0; - bookmark.map.center.x = dot(uvec, centerToTarget); - bookmark.map.center.y = dot(vvec, centerToTarget); - - bookmark.orbit.theta = 0; - bookmark.orbit.phi = 0; - bookmark.orbit.pivot = Base::mProps.targetPosition + - uvec * bookmark.map.center.x + - vvec * bookmark.map.center.y; - bookmark.orbit.distance = halfExtent / tan(fov / 2.0); - - return bookmark; - } - - Bookmark getHomeBookmark() const override { - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT width = Base::mProps.mapExtent.x; - const FLOAT height = Base::mProps.mapExtent.y; - const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL; - - Bookmark bookmark; - bookmark.mode = Mode::MAP; - bookmark.map.extent = horiz ? width : height; - bookmark.map.center.x = 0; - bookmark.map.center.y = 0; - - bookmark.orbit.theta = 0; - bookmark.orbit.phi = 0; - bookmark.orbit.pivot = Base::mTarget; - bookmark.orbit.distance = 0.5 * bookmark.map.extent / tan(fov / 2.0); - - // TODO: Add optional boundary constraints here. - - return bookmark; - } - - void jumpToBookmark(const Bookmark& bookmark) override { - const vec3 targetToEye = Base::mProps.groundPlane.xyz; - const FLOAT halfExtent = bookmark.map.extent / 2.0; - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT distance = halfExtent / tan(fov / 2.0); - vec3 uvec = cross(Base::mProps.upVector, targetToEye); - vec3 vvec = cross(targetToEye, uvec); - uvec = normalize(uvec) * bookmark.map.center.x; - vvec = normalize(vvec) * bookmark.map.center.y; - Base::mTarget = Base::mProps.targetPosition + uvec + vvec; - Base::mEye = Base::mTarget + distance * targetToEye; - } - -private: - bool raycastPlane(const vec3& origin, const vec3& dir, FLOAT* t) const { - const vec4 plane = Base::mProps.groundPlane; - const vec3 n = vec3(plane[0], plane[1], plane[2]); - const vec3 p0 = n * plane[3]; - const FLOAT denom = -dot(n, dir); - if (denom > 1e-6) { - const vec3 p0l0 = p0 - origin; - *t = dot(p0l0, n) / -denom; - return *t >= 0; - } - return false; - } - - void moveWithConstraints(vec3 eye, vec3 targetPosition) { - Base::mEye = eye; - Base::mTarget = targetPosition; - // TODO: Add optional boundary constraints here. - } - -private: - bool mGrabbing = false; - vec3 mGrabScene; - vec3 mGrabFar; - vec3 mGrabEye; - vec3 mGrabTarget; -}; - -} // namespace camutils -} // namespace filament - -#endif /* CAMUTILS_MAP_MANIPULATOR_H */ diff --git a/ios/include/src/OrbitManipulator.h b/ios/include/src/OrbitManipulator.h deleted file mode 100644 index 54e325cc..00000000 --- a/ios/include/src/OrbitManipulator.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CAMUTILS_ORBIT_MANIPULATOR_H -#define CAMUTILS_ORBIT_MANIPULATOR_H - -#include - -#include - -#define MAX_PHI (F_PI / 2.0 - 0.001) - -namespace filament { -namespace camutils { - -using namespace filament::math; - -template -class OrbitManipulator : public Manipulator { -public: - using vec2 = filament::math::vec2; - using vec3 = filament::math::vec3; - using vec4 = filament::math::vec4; - using Bookmark = filament::camutils::Bookmark; - using Base = Manipulator; - using Config = typename Base::Config; - - enum GrabState { INACTIVE, ORBITING, PANNING }; - - OrbitManipulator(Mode mode, const Config& props) : Base(mode, props) { - setProperties(props); - Base::mEye = Base::mProps.orbitHomePosition; - mPivot = Base::mTarget = Base::mProps.targetPosition; - } - - void setProperties(const Config& props) override { - Config resolved = props; - - if (resolved.orbitHomePosition == vec3(0)) { - resolved.orbitHomePosition = vec3(0, 0, 1); - } - - if (resolved.orbitSpeed == vec2(0)) { - resolved.orbitSpeed = vec2(0.01); - } - - // By default, place the ground plane so that it aligns with the targetPosition position. - // This is used only when PANNING. - if (resolved.groundPlane == vec4(0)) { - const FLOAT d = length(resolved.targetPosition); - const vec3 n = normalize(resolved.orbitHomePosition - resolved.targetPosition); - resolved.groundPlane = vec4(n, -d); - } - - Base::setProperties(resolved); - } - - void grabBegin(int x, int y, bool strafe) override { - mGrabState = strafe ? PANNING : ORBITING; - mGrabPivot = mPivot; - mGrabEye = Base::mEye; - mGrabTarget = Base::mTarget; - mGrabBookmark = getCurrentBookmark(); - mGrabWinX = x; - mGrabWinY = y; - mGrabFar = Base::raycastFarPlane(x, y); - Base::raycast(x, y, &mGrabScene); - } - - void grabUpdate(int x, int y) override { - const int delx = mGrabWinX - x; - const int dely = mGrabWinY - y; - - if (mGrabState == ORBITING) { - Bookmark bookmark = getCurrentBookmark(); - - const FLOAT theta = delx * Base::mProps.orbitSpeed.x; - const FLOAT phi = dely * Base::mProps.orbitSpeed.y; - const FLOAT maxPhi = MAX_PHI; - - bookmark.orbit.phi = clamp(mGrabBookmark.orbit.phi + phi, -maxPhi, +maxPhi); - bookmark.orbit.theta = mGrabBookmark.orbit.theta + theta; - - jumpToBookmark(bookmark); - } - - if (mGrabState == PANNING) { - const FLOAT ulen = distance(mGrabScene, mGrabEye); - const FLOAT vlen = distance(mGrabFar, mGrabScene); - const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen; - mPivot = mGrabPivot + translation; - Base::mEye = mGrabEye + translation; - Base::mTarget = mGrabTarget + translation; - } - } - - void grabEnd() override { - mGrabState = INACTIVE; - } - - void scroll(int x, int y, FLOAT scrolldelta) override { - const vec3 gaze = normalize(Base::mTarget - Base::mEye); - const vec3 movement = gaze * Base::mProps.zoomSpeed * -scrolldelta; - const vec3 v0 = mPivot - Base::mEye; - Base::mEye += movement; - Base::mTarget += movement; - const vec3 v1 = mPivot - Base::mEye; - - // Check if the camera has moved past the point of interest. - if (dot(v0, v1) < 0) { - mFlipped = !mFlipped; - } - } - - Bookmark getCurrentBookmark() const override { - Bookmark bookmark; - bookmark.mode = Mode::ORBIT; - const vec3 pivotToEye = Base::mEye - mPivot; - const FLOAT d = length(pivotToEye); - const FLOAT x = pivotToEye.x / d; - const FLOAT y = pivotToEye.y / d; - const FLOAT z = pivotToEye.z / d; - - bookmark.orbit.phi = asin(y); - bookmark.orbit.theta = atan2(x, z); - bookmark.orbit.distance = mFlipped ? -d : d; - bookmark.orbit.pivot = mPivot; - - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT halfExtent = d * tan(fov / 2.0); - const vec3 targetToEye = Base::mProps.groundPlane.xyz; - const vec3 uvec = cross(Base::mProps.upVector, targetToEye); - const vec3 vvec = cross(targetToEye, uvec); - const vec3 centerToTarget = mPivot - Base::mProps.targetPosition; - - bookmark.map.extent = halfExtent * 2; - bookmark.map.center.x = dot(uvec, centerToTarget); - bookmark.map.center.y = dot(vvec, centerToTarget); - - return bookmark; - } - - Bookmark getHomeBookmark() const override { - Bookmark bookmark; - bookmark.mode = Mode::ORBIT; - bookmark.orbit.phi = FLOAT(0); - bookmark.orbit.theta = FLOAT(0); - bookmark.orbit.pivot = Base::mProps.targetPosition; - bookmark.orbit.distance = distance(Base::mProps.targetPosition, Base::mProps.orbitHomePosition); - - const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0; - const FLOAT halfExtent = bookmark.orbit.distance * tan(fov / 2.0); - - bookmark.map.extent = halfExtent * 2; - bookmark.map.center.x = 0; - bookmark.map.center.y = 0; - - return bookmark; - } - - void jumpToBookmark(const Bookmark& bookmark) override { - mPivot = bookmark.orbit.pivot; - const FLOAT x = sin(bookmark.orbit.theta) * cos(bookmark.orbit.phi); - const FLOAT y = sin(bookmark.orbit.phi); - const FLOAT z = cos(bookmark.orbit.theta) * cos(bookmark.orbit.phi); - Base::mEye = mPivot + vec3(x, y, z) * abs(bookmark.orbit.distance); - mFlipped = bookmark.orbit.distance < 0; - Base::mTarget = Base::mEye + vec3(x, y, z) * (mFlipped ? 1.0 : -1.0); - } - -private: - GrabState mGrabState = INACTIVE; - bool mFlipped = false; - vec3 mGrabPivot; - vec3 mGrabScene; - vec3 mGrabFar; - vec3 mGrabEye; - vec3 mGrabTarget; - Bookmark mGrabBookmark; - int mGrabWinX; - int mGrabWinY; - vec3 mPivot; -}; - -} // namespace camutils -} // namespace filament - -#endif /* CAMUTILS_ORBIT_MANIPULATOR_H */ diff --git a/ios/include/tests/test_camutils.cpp b/ios/include/tests/test_camutils.cpp deleted file mode 100644 index ac2314d6..00000000 --- a/ios/include/tests/test_camutils.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include - -using namespace filament::math; - -namespace camutils = filament::camutils; - -using CamManipulator = camutils::Manipulator; - -class CamUtilsTest : public testing::Test {}; - -#define EXPECT_VEC_EQ(a, x, y, z) expectVecEq(a, {x, y, z}, __LINE__) - -static void expectVecEq(float3 a, float3 b, int line) { - EXPECT_FLOAT_EQ(a.x, b.x); - EXPECT_FLOAT_EQ(a.y, b.y); - EXPECT_FLOAT_EQ(a.z, b.z); -} - -TEST_F(CamUtilsTest, Orbit) { - - float3 eye, targetPosition, up; - - CamManipulator* orbit = CamManipulator::Builder() - .viewport(256, 256) - .targetPosition(0, 0, 0) - .upVector(0, 1, 0) - .zoomSpeed(0.01) - .orbitHomePosition(0, 0, 4) - .orbitSpeed(1, 1) - .build(camutils::Mode::ORBIT); - - orbit->getLookAt(&eye, &targetPosition, &up); - EXPECT_VEC_EQ(eye, 0, 0, 4); - EXPECT_VEC_EQ(targetPosition, 0, 0, 0); - EXPECT_VEC_EQ(up, 0, 1, 0); - - orbit->grabBegin(100, 100, false); - orbit->grabUpdate(200, 100); - orbit->grabEnd(); - - orbit->getLookAt(&eye, &targetPosition, &up); - EXPECT_VEC_EQ(eye, 2.0254626, 0, 3.4492755); - EXPECT_VEC_EQ(targetPosition, 1.519097, 0, 2.5869565); - EXPECT_VEC_EQ(up, 0, 1, 0); - - delete orbit; -} - -TEST_F(CamUtilsTest, Map) { - - float3 eye, targetPosition, up; - - CamManipulator* map = CamManipulator::Builder() - .viewport(256, 256) - .targetPosition(0, 0, 0) - .zoomSpeed(0.01) - .orbitHomePosition(0, 0, 4) - .build(camutils::Mode::MAP); - - delete map; -} - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/ios/include/trie/array-hash/array_growth_policy.h b/ios/include/trie/array-hash/array_growth_policy.h deleted file mode 100644 index 641e0cb7..00000000 --- a/ios/include/trie/array-hash/array_growth_policy.h +++ /dev/null @@ -1,307 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_GROWTH_POLICY_H -#define TSL_ARRAY_GROWTH_POLICY_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef __EXCEPTIONS -# define THROW(_e, _m) throw _e(_m) -#else -# include -# ifndef NDEBUG -# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0) -# else -# define THROW(_e, _m) std::terminate() -# endif -#endif - - -namespace tsl { -namespace ah { - -/** - * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows - * the table to use a mask operation instead of a modulo operation to map a hash to a bucket. - * - * GrowthFactor must be a power of two >= 2. - */ -template -class power_of_two_growth_policy { -public: - /** - * Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter. - * This number is a minimum, the policy may update this value with a higher value if needed (but not lower). - * - * If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and - * bucket_for_hash must always return 0 in this case. - */ - explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) { - if(min_bucket_count_in_out > max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(min_bucket_count_in_out > 0) { - min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out); - m_mask = min_bucket_count_in_out - 1; - } - else { - m_mask = 0; - } - } - - /** - * Return the bucket [0, bucket_count()) to which the hash belongs. - * If bucket_count() is 0, it must always return 0. - */ - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return hash & m_mask; - } - - /** - * Return the number of buckets that should be used on next growth. - */ - std::size_t next_bucket_count() const { - if((m_mask + 1) > max_bucket_count() / GrowthFactor) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - return (m_mask + 1) * GrowthFactor; - } - - /** - * Return the maximum number of buckets supported by the policy. - */ - std::size_t max_bucket_count() const { - // Largest power of two. - return (std::numeric_limits::max() / 2) + 1; - } - - /** - * Reset the growth policy as if it was created with a bucket count of 0. - * After a clear, the policy must always return 0 when bucket_for_hash is called. - */ - void clear() noexcept { - m_mask = 0; - } - -private: - static std::size_t round_up_to_power_of_two(std::size_t value) { - if(is_power_of_two(value)) { - return value; - } - - if(value == 0) { - return 1; - } - - --value; - for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { - value |= value >> i; - } - - return value + 1; - } - - static constexpr bool is_power_of_two(std::size_t value) { - return value != 0 && (value & (value - 1)) == 0; - } - -protected: - static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2."); - - std::size_t m_mask; -}; - - -/** - * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash - * to a bucket. Slower but it can be useful if you want a slower growth. - */ -template> -class mod_growth_policy { -public: - explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) { - if(min_bucket_count_in_out > max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(min_bucket_count_in_out > 0) { - m_mod = min_bucket_count_in_out; - } - else { - m_mod = 1; - } - } - - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return hash % m_mod; - } - - std::size_t next_bucket_count() const { - if(m_mod == max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR); - if(!std::isnormal(next_bucket_count)) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(next_bucket_count > double(max_bucket_count())) { - return max_bucket_count(); - } - else { - return std::size_t(next_bucket_count); - } - } - - std::size_t max_bucket_count() const { - return MAX_BUCKET_COUNT; - } - - void clear() noexcept { - m_mod = 1; - } - -private: - static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den; - static const std::size_t MAX_BUCKET_COUNT = - std::size_t(double( - std::numeric_limits::max() / REHASH_SIZE_MULTIPLICATION_FACTOR - )); - - static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1."); - - std::size_t m_mod; -}; - - - -namespace detail { - -static constexpr const std::array PRIMES = {{ - 1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, - 1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, - 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, - 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul -}}; - -template -static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; } - -// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the -// compiler can optimize the modulo code better with a constant known at the compilation. -static constexpr const std::array MOD_PRIME = {{ - &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, - &mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>, - &mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, - &mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39> -}}; - -} - -/** - * Grow the hash table by using prime numbers as bucket count. Slower than tsl::ah::power_of_two_growth_policy in - * general but will probably distribute the values around better in the buckets with a poor hash function. - * - * To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers. - * - * With a switch the code would look like: - * \code - * switch(iprime) { // iprime is the current prime of the hash table - * case 0: hash % 5ul; - * break; - * case 1: hash % 17ul; - * break; - * case 2: hash % 29ul; - * break; - * ... - * } - * \endcode - * - * Due to the constant variable in the modulo the compiler is able to optimize the operation - * by a series of multiplications, substractions and shifts. - * - * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment. - */ -class prime_growth_policy { -public: - explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) { - auto it_prime = std::lower_bound(detail::PRIMES.begin(), - detail::PRIMES.end(), min_bucket_count_in_out); - if(it_prime == detail::PRIMES.end()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - m_iprime = static_cast(std::distance(detail::PRIMES.begin(), it_prime)); - if(min_bucket_count_in_out > 0) { - min_bucket_count_in_out = *it_prime; - } - else { - min_bucket_count_in_out = 0; - } - } - - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return detail::MOD_PRIME[m_iprime](hash); - } - - std::size_t next_bucket_count() const { - if(m_iprime + 1 >= detail::PRIMES.size()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - return detail::PRIMES[m_iprime + 1]; - } - - std::size_t max_bucket_count() const { - return detail::PRIMES.back(); - } - - void clear() noexcept { - m_iprime = 0; - } - -private: - unsigned int m_iprime; - - static_assert(std::numeric_limits::max() >= detail::PRIMES.size(), - "The type of m_iprime is not big enough."); -}; - -} -} - -#endif diff --git a/ios/include/trie/array-hash/array_hash.h b/ios/include/trie/array-hash/array_hash.h deleted file mode 100644 index ccb204ca..00000000 --- a/ios/include/trie/array-hash/array_hash.h +++ /dev/null @@ -1,1766 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_HASH_H -#define TSL_ARRAY_HASH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array_growth_policy.h" - - -/* - * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433), - * check also __cplusplus version. - */ -#ifdef __has_include -# if __has_include() && __cplusplus >= 201703L -# define TSL_AH_HAS_STRING_VIEW -# endif -#endif - - -#ifdef TSL_AH_HAS_STRING_VIEW -# include -#endif - - -#ifdef TSL_DEBUG -# define tsl_ah_assert(expr) assert(expr) -#else -# define tsl_ah_assert(expr) (static_cast(0)) -#endif - - - -/** - * Implementation of the array hash structure described in the - * "Cache-conscious collision resolution in string hash tables." (Askitis Nikolas and Justin Zobel, 2005) paper. - */ -namespace tsl { - -namespace ah { - -template -struct str_hash { -#ifdef TSL_AH_HAS_STRING_VIEW - std::size_t operator()(const CharT* key, std::size_t key_size) const { - return std::hash>()(std::basic_string_view(key, key_size)); - } -#else - /** - * FNV-1a hash - */ - std::size_t operator()(const CharT* key, std::size_t key_size) const { - static const std::size_t init = std::size_t((sizeof(std::size_t) == 8)?0xcbf29ce484222325:0x811c9dc5); - static const std::size_t multiplier = std::size_t((sizeof(std::size_t) == 8)?0x100000001b3:0x1000193); - - std::size_t hash = init; - for (std::size_t i = 0; i < key_size; ++i) { - hash ^= key[i]; - hash *= multiplier; - } - - return hash; - } -#endif -}; - -template -struct str_equal { - bool operator()(const CharT* key_lhs, std::size_t key_size_lhs, - const CharT* key_rhs, std::size_t key_size_rhs) const - { - if(key_size_lhs != key_size_rhs) { - return false; - } - else { - return std::memcmp(key_lhs, key_rhs, key_size_lhs * sizeof(CharT)) == 0; - } - } -}; -} - - -namespace detail_array_hash { - -template -struct is_iterator: std::false_type { -}; - -template -struct is_iterator::iterator_category, void>::value>::type>: std::true_type { -}; - -static constexpr bool is_power_of_two(std::size_t value) { - return value != 0 && (value & (value - 1)) == 0; -} - -template -static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { - T ret = static_cast(value); - if(static_cast(ret) != value) { - THROW(std::runtime_error, error_message); - } - - const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || - (std::is_signed::value && std::is_signed::value); - if(!is_same_signedness && (ret < T{}) != (value < U{})) { - THROW(std::runtime_error, error_message); - } - - return ret; -} - - - -/** - * Fixed size type used to represent size_type values on serialization. Need to be big enough - * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. - */ -using slz_size_type = std::uint64_t; - -template -static T deserialize_value(Deserializer& deserializer) { - // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword -#if defined (_MSC_VER) && _MSC_VER < 1910 - return deserializer.Deserializer::operator()(); -#else - return deserializer.Deserializer::template operator()(); -#endif -} - -/** - * For each string in the bucket, store the size of the string, the chars of the string - * and T, if it's not void. T should be either void or an unsigned type. - * - * End the buffer with END_OF_BUCKET flag. END_OF_BUCKET has the same type as the string size variable. - * - * m_buffer (CharT*): - * | size of str1 (KeySizeT) | str1 (const CharT*) | value (T if T != void) | ... | - * | size of strN (KeySizeT) | strN (const CharT*) | value (T if T != void) | END_OF_BUCKET (KeySizeT) | - * - * m_buffer is null if there is no string in the bucket. - * - * KeySizeT and T are extended to be a multiple of CharT when stored in the buffer. - * - * Use std::malloc and std::free instead of new and delete so we can have access to std::realloc. - */ -template -class array_bucket { - template - using has_mapped_type = typename std::integral_constant::value>; - - static_assert(!has_mapped_type::value || std::is_unsigned::value, - "T should be either void or an unsigned type."); - - static_assert(std::is_unsigned::value, "KeySizeT should be an unsigned type."); - -public: - template - class array_bucket_iterator; - - using char_type = CharT; - using key_size_type = KeySizeT; - using mapped_type = T; - using size_type = std::size_t; - using key_equal = KeyEqual; - using iterator = array_bucket_iterator; - using const_iterator = array_bucket_iterator; - - static_assert(sizeof(KeySizeT) <= sizeof(size_type), "sizeof(KeySizeT) should be <= sizeof(std::size_t;)"); - static_assert(std::is_unsigned::value, ""); - -private: - /** - * Return how much space in bytes the type U will take when stored in the buffer. - * As the buffer is of type CharT, U may take more space than sizeof(U). - * - * Example: sizeof(CharT) = 4, sizeof(U) = 2 => U will take 4 bytes in the buffer instead of 2. - */ - template - static constexpr size_type sizeof_in_buff() noexcept { - static_assert(is_power_of_two(sizeof(U)), "sizeof(U) should be a power of two."); - static_assert(is_power_of_two(sizeof(CharT)), "sizeof(CharT) should be a power of two."); - - return std::max(sizeof(U), sizeof(CharT)); - } - - /** - * Same as sizeof_in_buff, but instead of returning the size in bytes return it in term of sizeof(CharT). - */ - template - static constexpr size_type size_as_char_t() noexcept { - return sizeof_in_buff() / sizeof(CharT); - } - - static key_size_type read_key_size(const CharT* buffer) noexcept { - key_size_type key_size; - std::memcpy(&key_size, buffer, sizeof(key_size)); - - return key_size; - } - - static mapped_type read_value(const CharT* buffer) noexcept { - mapped_type value; - std::memcpy(&value, buffer, sizeof(value)); - - return value; - } - - static bool is_end_of_bucket(const CharT* buffer) noexcept { - return read_key_size(buffer) == END_OF_BUCKET; - } - -public: - /** - * Return the size required for an entry with a key of size 'key_size'. - */ - template::value>::type* = nullptr> - static size_type entry_required_bytes(size_type key_size) noexcept { - return sizeof_in_buff() + (key_size + KEY_EXTRA_SIZE) * sizeof(CharT); - } - - template::value>::type* = nullptr> - static size_type entry_required_bytes(size_type key_size) noexcept { - return sizeof_in_buff() + (key_size + KEY_EXTRA_SIZE) * sizeof(CharT) + - sizeof_in_buff(); - } - -private: - /** - * Return the size of the current entry in buffer. - */ - static size_type entry_size_bytes(const CharT* buffer) noexcept { - return entry_required_bytes(read_key_size(buffer)); - } - -public: - template - class array_bucket_iterator { - friend class array_bucket; - - using buffer_type = typename std::conditional::type; - - explicit array_bucket_iterator(buffer_type* position) noexcept: m_position(position) { - } - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = void; - using difference_type = std::ptrdiff_t; - using reference = void; - using pointer = void; - - public: - array_bucket_iterator() noexcept: m_position(nullptr) { - } - - const CharT* key() const { - return m_position + size_as_char_t(); - } - - size_type key_size() const { - return read_key_size(m_position); - } - - template::value>::type* = nullptr> - U value() const { - return read_value(m_position + size_as_char_t() + key_size() + KEY_EXTRA_SIZE); - } - - - template::value && !IsConst && std::is_same::value>::type* = nullptr> - void set_value(U value) noexcept { - std::memcpy(m_position + size_as_char_t() + key_size() + KEY_EXTRA_SIZE, - &value, sizeof(value)); - } - - array_bucket_iterator& operator++() { - m_position += entry_size_bytes(m_position)/sizeof(CharT); - if(is_end_of_bucket(m_position)) { - m_position = nullptr; - } - - return *this; - } - - array_bucket_iterator operator++(int) { - array_bucket_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const array_bucket_iterator& lhs, const array_bucket_iterator& rhs) { - return lhs.m_position == rhs.m_position; - } - - friend bool operator!=(const array_bucket_iterator& lhs, const array_bucket_iterator& rhs) { - return !(lhs == rhs); - } - - private: - buffer_type* m_position; - }; - - - - static iterator end_it() noexcept { - return iterator(nullptr); - } - - static const_iterator cend_it() noexcept { - return const_iterator(nullptr); - } - -public: - array_bucket(): m_buffer(nullptr) { - } - - /** - * Reserve 'size' in the buffer of the bucket. The created bucket is empty. - */ - array_bucket(std::size_t size): m_buffer(nullptr) { - if(size == 0) { - return; - } - - m_buffer = static_cast(std::malloc(size*sizeof(CharT) + sizeof_in_buff())); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(m_buffer, &end_of_bucket, sizeof(end_of_bucket)); - } - - ~array_bucket() { - clear(); - } - - array_bucket(const array_bucket& other) { - if(other.m_buffer == nullptr) { - m_buffer = nullptr; - return; - } - - const size_type other_buffer_size = other.size(); - m_buffer = static_cast(std::malloc(other_buffer_size*sizeof(CharT) + sizeof_in_buff())); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - std::memcpy(m_buffer, other.m_buffer, other_buffer_size*sizeof(CharT)); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(m_buffer + other_buffer_size, &end_of_bucket, sizeof(end_of_bucket)); - } - - array_bucket(array_bucket&& other) noexcept: m_buffer(other.m_buffer) { - other.m_buffer = nullptr; - } - - array_bucket& operator=(array_bucket other) noexcept { - other.swap(*this); - - return *this; - } - - void swap(array_bucket& other) noexcept { - std::swap(m_buffer, other.m_buffer); - } - - iterator begin() noexcept { return iterator(m_buffer); } - iterator end() noexcept { return iterator(nullptr); } - const_iterator begin() const noexcept { return cbegin(); } - const_iterator end() const noexcept { return cend(); } - const_iterator cbegin() const noexcept { return const_iterator(m_buffer); } - const_iterator cend() const noexcept { return const_iterator(nullptr); } - - /** - * Return an iterator pointing to the key entry if presents or, if not there, to the position - * past the last element of the bucket. Return end() if the bucket has not be initialized yet. - * - * The boolean of the pair is set to true if the key is there, false otherwise. - */ - std::pair find_or_end_of_bucket(const CharT* key, size_type key_size) const noexcept { - if(m_buffer == nullptr) { - return std::make_pair(cend(), false); - } - - const CharT* buffer_ptr_in_out = m_buffer; - const bool found = find_or_end_of_bucket_impl(key, key_size, buffer_ptr_in_out); - - return std::make_pair(const_iterator(buffer_ptr_in_out), found); - } - - /** - * Append the element 'key' with its potential value at the end of the bucket. - * 'end_of_bucket' should point past the end of the last element in the bucket, end() if the bucket - * was not initialized yet. You usually get this value from find_or_end_of_bucket. - * - * Return the position where the element was actually inserted. - */ - template - const_iterator append(const_iterator end_of_bucket, const CharT* key, size_type key_size, - ValueArgs&&... value) - { - const key_size_type key_sz = as_key_size_type(key_size); - - if(end_of_bucket == cend()) { - tsl_ah_assert(m_buffer == nullptr); - - const size_type buffer_size = entry_required_bytes(key_sz) + sizeof_in_buff(); - - m_buffer = static_cast(std::malloc(buffer_size)); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - append_impl(key, key_sz, m_buffer, std::forward(value)...); - - return const_iterator(m_buffer); - } - else { - tsl_ah_assert(is_end_of_bucket(end_of_bucket.m_position)); - - const size_type current_size = ((end_of_bucket.m_position + size_as_char_t()) - - m_buffer) * sizeof(CharT); - const size_type new_size = current_size + entry_required_bytes(key_sz); - - - CharT* new_buffer = static_cast(std::realloc(m_buffer, new_size)); - if(new_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - m_buffer = new_buffer; - - - CharT* buffer_append_pos = m_buffer + current_size / sizeof(CharT) - - size_as_char_t(); - append_impl(key, key_sz, buffer_append_pos, std::forward(value)...); - - return const_iterator(buffer_append_pos); - } - - } - - const_iterator erase(const_iterator position) noexcept { - tsl_ah_assert(position.m_position != nullptr && !is_end_of_bucket(position.m_position)); - - // get mutable pointers - CharT* start_entry = m_buffer + (position.m_position - m_buffer); - CharT* start_next_entry = start_entry + entry_size_bytes(start_entry) / sizeof(CharT); - - - CharT* end_buffer_ptr = start_next_entry; - while(!is_end_of_bucket(end_buffer_ptr)) { - end_buffer_ptr += entry_size_bytes(end_buffer_ptr) / sizeof(CharT); - } - end_buffer_ptr += size_as_char_t(); - - - const size_type size_to_move = (end_buffer_ptr - start_next_entry) * sizeof(CharT); - std::memmove(start_entry, start_next_entry, size_to_move); - - - if(is_end_of_bucket(m_buffer)) { - clear(); - return cend(); - } - else if(is_end_of_bucket(start_entry)) { - return cend(); - } - else { - return const_iterator(start_entry); - } - } - - /** - * Return true if an element has been erased - */ - bool erase(const CharT* key, size_type key_size) noexcept { - if(m_buffer == nullptr) { - return false; - } - - const CharT* entry_buffer_ptr_in_out = m_buffer; - bool found = find_or_end_of_bucket_impl(key, key_size, entry_buffer_ptr_in_out); - if(found) { - erase(const_iterator(entry_buffer_ptr_in_out)); - - return true; - } - else { - return false; - } - } - - /** - * Bucket should be big enough and there is no check to see if the key already exists. - * No check on key_size. - */ - template - void append_in_reserved_bucket_no_check(const CharT* key, size_type key_size, ValueArgs&&... value) noexcept { - CharT* buffer_ptr = m_buffer; - while(!is_end_of_bucket(buffer_ptr)) { - buffer_ptr += entry_size_bytes(buffer_ptr)/sizeof(CharT); - } - - append_impl(key, key_size_type(key_size), buffer_ptr, std::forward(value)...); - } - - bool empty() const noexcept { - return m_buffer == nullptr || is_end_of_bucket(m_buffer); - } - - void clear() noexcept { - std::free(m_buffer); - m_buffer = nullptr; - } - - iterator mutable_iterator(const_iterator pos) noexcept { - return iterator(m_buffer + (pos.m_position - m_buffer)); - } - - template - void serialize(Serializer& serializer) const { - const slz_size_type bucket_size = size(); - tsl_ah_assert(m_buffer != nullptr || bucket_size == 0); - - serializer(bucket_size); - serializer(m_buffer, bucket_size); - } - - template - static array_bucket deserialize(Deserializer& deserializer) { - array_bucket bucket; - const slz_size_type bucket_size_ds = deserialize_value(deserializer); - - if(bucket_size_ds == 0) { - return bucket; - } - - const std::size_t bucket_size = numeric_cast(bucket_size_ds, "Deserialized bucket_size is too big."); - bucket.m_buffer = static_cast(std::malloc(bucket_size*sizeof(CharT) + sizeof_in_buff())); - if(bucket.m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - - deserializer(bucket.m_buffer, bucket_size); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(bucket.m_buffer + bucket_size, &end_of_bucket, sizeof(end_of_bucket)); - - - tsl_ah_assert(bucket.size() == bucket_size); - return bucket; - } - -private: - key_size_type as_key_size_type(size_type key_size) const { - if(key_size > MAX_KEY_SIZE) { - THROW(std::length_error, "Key is too long."); - } - - return key_size_type(key_size); - } - - /* - * Return true if found, false otherwise. - * If true, buffer_ptr_in_out points to the start of the entry matching 'key'. - * If false, buffer_ptr_in_out points to where the 'key' should be inserted. - * - * Start search from buffer_ptr_in_out. - */ - bool find_or_end_of_bucket_impl(const CharT* key, size_type key_size, - const CharT* & buffer_ptr_in_out) const noexcept - { - while(!is_end_of_bucket(buffer_ptr_in_out)) { - const key_size_type buffer_key_size = read_key_size(buffer_ptr_in_out); - const CharT* buffer_str = buffer_ptr_in_out + size_as_char_t(); - if(KeyEqual()(buffer_str, buffer_key_size, key, key_size)) { - return true; - } - - buffer_ptr_in_out += entry_size_bytes(buffer_ptr_in_out)/sizeof(CharT); - } - - return false; - } - - template::value>::type* = nullptr> - void append_impl(const CharT* key, key_size_type key_size, CharT* buffer_append_pos) noexcept { - std::memcpy(buffer_append_pos, &key_size, sizeof(key_size)); - buffer_append_pos += size_as_char_t(); - - std::memcpy(buffer_append_pos, key, key_size * sizeof(CharT)); - buffer_append_pos += key_size; - - const CharT zero = 0; - std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE * sizeof(CharT)); - buffer_append_pos += KEY_EXTRA_SIZE; - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(buffer_append_pos, &end_of_bucket, sizeof(end_of_bucket)); - } - - template::value>::type* = nullptr> - void append_impl(const CharT* key, key_size_type key_size, CharT* buffer_append_pos, - typename array_bucket::mapped_type value) noexcept - { - std::memcpy(buffer_append_pos, &key_size, sizeof(key_size)); - buffer_append_pos += size_as_char_t(); - - std::memcpy(buffer_append_pos, key, key_size * sizeof(CharT)); - buffer_append_pos += key_size; - - const CharT zero = 0; - std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE * sizeof(CharT)); - buffer_append_pos += KEY_EXTRA_SIZE; - - std::memcpy(buffer_append_pos, &value, sizeof(value)); - buffer_append_pos += size_as_char_t(); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(buffer_append_pos, &end_of_bucket, sizeof(end_of_bucket)); - } - - /** - * Return the number of CharT in m_buffer. As the size of the buffer is not stored to gain some space, - * the method need to find the EOF marker and is thus in O(n). - */ - size_type size() const noexcept { - if(m_buffer == nullptr) { - return 0; - } - - CharT* buffer_ptr = m_buffer; - while(!is_end_of_bucket(buffer_ptr)) { - buffer_ptr += entry_size_bytes(buffer_ptr)/sizeof(CharT); - } - - return buffer_ptr - m_buffer; - } - -private: - static const key_size_type END_OF_BUCKET = std::numeric_limits::max(); - static const key_size_type KEY_EXTRA_SIZE = StoreNullTerminator?1:0; - - CharT* m_buffer; - -public: - static const key_size_type MAX_KEY_SIZE = - // -1 for END_OF_BUCKET - key_size_type(std::numeric_limits::max() - KEY_EXTRA_SIZE - 1); -}; - - -template -class value_container { -public: - void clear() noexcept { - m_values.clear(); - } - - void reserve(std::size_t new_cap) { - m_values.reserve(new_cap); - } - - void shrink_to_fit() { - m_values.shrink_to_fit(); - } - - friend void swap(value_container& lhs, value_container& rhs) { - lhs.m_values.swap(rhs.m_values); - } - -protected: - static constexpr float VECTOR_GROWTH_RATE = 1.5f; - - // TODO use a sparse array? or a std::deque - std::vector m_values; -}; - -template<> -class value_container { -public: - void clear() noexcept { - } - - void shrink_to_fit() { - } - - void reserve(std::size_t /*new_cap*/) { - } -}; - - - -/** - * If there is no value in the array_hash (in the case of a set for example), T should be void. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * - * The number of elements in the map is limited to std::numeric_limits::max(). - */ -template -class array_hash: private value_container, private Hash, private GrowthPolicy { -private: - template - using has_mapped_type = typename std::integral_constant::value>; - - /** - * If there is a mapped type in array_hash, we store the values in m_values of value_container class - * and we store an index to m_values in the bucket. The index is of type IndexSizeT. - */ - using array_bucket = tsl::detail_array_hash::array_bucket::value, - IndexSizeT, - void>::type, - KeyEqual, KeySizeT, StoreNullTerminator>; - -public: - template - class array_hash_iterator; - - using char_type = CharT; - using key_size_type = KeySizeT; - using index_size_type = IndexSizeT; - using size_type = std::size_t; - using hasher = Hash; - using key_equal = KeyEqual; - using iterator = array_hash_iterator; - using const_iterator = array_hash_iterator; - - -/* - * Iterator classes - */ -public: - template - class array_hash_iterator { - friend class array_hash; - - private: - using iterator_array_bucket = typename array_bucket::const_iterator; - - using iterator_buckets = typename std::conditional::const_iterator, - typename std::vector::iterator>::type; - - using array_hash_ptr = typename std::conditional::type; - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = typename std::conditional::value, T, void>::type; - using difference_type = std::ptrdiff_t; - using reference = typename std::conditional::value, - typename std::conditional< - IsConst, - typename std::add_lvalue_reference::type, - typename std::add_lvalue_reference::type>::type, - void>::type; - using pointer = typename std::conditional::value, - typename std::conditional::type, - void>::type; - - - private: - array_hash_iterator(iterator_buckets buckets_iterator, iterator_array_bucket array_bucket_iterator, - array_hash_ptr array_hash_p) noexcept: - m_buckets_iterator(buckets_iterator), - m_array_bucket_iterator(array_bucket_iterator), - m_array_hash(array_hash_p) - { - tsl_ah_assert(m_array_hash != nullptr); - } - - public: - array_hash_iterator() noexcept: m_array_hash(nullptr) { - } - - template::type* = nullptr> - array_hash_iterator(const array_hash_iterator& other) noexcept : - m_buckets_iterator(other.m_buckets_iterator), - m_array_bucket_iterator(other.m_array_bucket_iterator), - m_array_hash(other.m_array_hash) - { - } - - array_hash_iterator(const array_hash_iterator& other) = default; - array_hash_iterator(array_hash_iterator&& other) = default; - array_hash_iterator& operator=(const array_hash_iterator& other) = default; - array_hash_iterator& operator=(array_hash_iterator&& other) = default; - - const CharT* key() const { - return m_array_bucket_iterator.key(); - } - - size_type key_size() const { - return m_array_bucket_iterator.key_size(); - } - -#ifdef TSL_AH_HAS_STRING_VIEW - std::basic_string_view key_sv() const { - return std::basic_string_view(key(), key_size()); - } -#endif - - template::value>::type* = nullptr> - reference value() const { - return this->m_array_hash->m_values[value_position()]; - } - - template::value>::type* = nullptr> - reference operator*() const { - return value(); - } - - template::value>::type* = nullptr> - pointer operator->() const { - return std::addressof(value()); - } - - array_hash_iterator& operator++() { - tsl_ah_assert(m_buckets_iterator != m_array_hash->m_buckets_data.end()); - tsl_ah_assert(m_array_bucket_iterator != m_buckets_iterator->cend()); - - ++m_array_bucket_iterator; - if(m_array_bucket_iterator == m_buckets_iterator->cend()) { - do { - ++m_buckets_iterator; - } while(m_buckets_iterator != m_array_hash->m_buckets_data.end() && - m_buckets_iterator->empty()); - - if(m_buckets_iterator != m_array_hash->m_buckets_data.end()) { - m_array_bucket_iterator = m_buckets_iterator->cbegin(); - } - } - - return *this; - } - - array_hash_iterator operator++(int) { - array_hash_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const array_hash_iterator& lhs, const array_hash_iterator& rhs) { - return lhs.m_buckets_iterator == rhs.m_buckets_iterator && - lhs.m_array_bucket_iterator == rhs.m_array_bucket_iterator && - lhs.m_array_hash == rhs.m_array_hash; - } - - friend bool operator!=(const array_hash_iterator& lhs, const array_hash_iterator& rhs) { - return !(lhs == rhs); - } - - private: - template::value>::type* = nullptr> - IndexSizeT value_position() const { - return this->m_array_bucket_iterator.value(); - } - - private: - iterator_buckets m_buckets_iterator; - iterator_array_bucket m_array_bucket_iterator; - - array_hash_ptr m_array_hash; - }; - - - -public: - array_hash(size_type bucket_count, - const Hash& hash, - float max_load_factor): value_container(), - Hash(hash), - GrowthPolicy(bucket_count), - m_buckets_data(bucket_count > max_bucket_count()? - max_bucket_count(): - bucket_count), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(0) - { - this->max_load_factor(max_load_factor); - } - - array_hash(const array_hash& other): value_container(other), - Hash(other), - GrowthPolicy(other), - m_buckets_data(other.m_buckets_data), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(other.m_nb_elements), - m_max_load_factor(other.m_max_load_factor), - m_load_threshold(other.m_load_threshold) - { - } - - array_hash(array_hash&& other) noexcept(std::is_nothrow_move_constructible>::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible>::value) - : value_container(std::move(other)), - Hash(std::move(other)), - GrowthPolicy(std::move(other)), - m_buckets_data(std::move(other.m_buckets_data)), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(other.m_nb_elements), - m_max_load_factor(other.m_max_load_factor), - m_load_threshold(other.m_load_threshold) - { - other.value_container::clear(); - other.GrowthPolicy::clear(); - other.m_buckets_data.clear(); - other.m_buckets = static_empty_bucket_ptr(); - other.m_nb_elements = 0; - other.m_load_threshold = 0; - } - - array_hash& operator=(const array_hash& other) { - if(&other != this) { - value_container::operator=(other); - Hash::operator=(other); - GrowthPolicy::operator=(other); - - m_buckets_data = other.m_buckets_data; - m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr(): - m_buckets_data.data(); - m_nb_elements = other.m_nb_elements; - m_max_load_factor = other.m_max_load_factor; - m_load_threshold = other.m_load_threshold; - } - - return *this; - } - - array_hash& operator=(array_hash&& other) { - other.swap(*this); - other.clear(); - - return *this; - } - - - /* - * Iterators - */ - iterator begin() noexcept { - auto begin = m_buckets_data.begin(); - while(begin != m_buckets_data.end() && begin->empty()) { - ++begin; - } - - return (begin != m_buckets_data.end())?iterator(begin, begin->cbegin(), this):end(); - } - - const_iterator begin() const noexcept { - return cbegin(); - } - - const_iterator cbegin() const noexcept { - auto begin = m_buckets_data.cbegin(); - while(begin != m_buckets_data.cend() && begin->empty()) { - ++begin; - } - - return (begin != m_buckets_data.cend())?const_iterator(begin, begin->cbegin(), this):cend(); - } - - iterator end() noexcept { - return iterator(m_buckets_data.end(), array_bucket::cend_it(), this); - } - - const_iterator end() const noexcept { - return cend(); - } - - const_iterator cend() const noexcept { - return const_iterator(m_buckets_data.end(), array_bucket::cend_it(), this); - } - - - /* - * Capacity - */ - bool empty() const noexcept { - return m_nb_elements == 0; - } - - size_type size() const noexcept { - return m_nb_elements; - } - - size_type max_size() const noexcept { - return std::numeric_limits::max(); - } - - size_type max_key_size() const noexcept { - return MAX_KEY_SIZE; - } - - void shrink_to_fit() { - clear_old_erased_values(); - value_container::shrink_to_fit(); - - rehash_impl(size_type(std::ceil(float(size())/max_load_factor()))); - } - - /* - * Modifiers - */ - void clear() noexcept { - value_container::clear(); - - for(auto& bucket: m_buckets_data) { - bucket.clear(); - } - - m_nb_elements = 0; - } - - - - template - std::pair emplace(const CharT* key, size_type key_size, ValueArgs&&... value_args) { - const std::size_t hash = hash_key(key, key_size); - std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it_find.first, this), false); - } - - if(grow_on_high_load()) { - ibucket = bucket_for_hash(hash); - it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - } - - return emplace_impl(ibucket, it_find.first, key, key_size, std::forward(value_args)...); - } - - template - std::pair insert_or_assign(const CharT* key, size_type key_size, M&& obj) { - auto it = emplace(key, key_size, std::forward(obj)); - if(!it.second) { - it.first.value() = std::forward(obj); - } - - return it; - } - - - - iterator erase(const_iterator pos) { - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - return erase_from_bucket(mutable_iterator(pos)); - } - - iterator erase(const_iterator first, const_iterator last) { - if(first == last) { - return mutable_iterator(first); - } - - /** - * When erasing an element from a bucket with erase_from_bucket, it invalidates all the iterators - * in the array bucket of the element (m_array_bucket_iterator) but not the iterators of the buckets - * itself (m_buckets_iterator). - * - * So first erase all the values between first and last which are not part of the bucket of last, - * and then erase carefully the values in last's bucket. - */ - auto to_delete = mutable_iterator(first); - while(to_delete.m_buckets_iterator != last.m_buckets_iterator) { - to_delete = erase_from_bucket(to_delete); - } - - std::size_t nb_elements_until_last = std::distance(to_delete.m_array_bucket_iterator, - last.m_array_bucket_iterator); - while(nb_elements_until_last > 0) { - to_delete = erase_from_bucket(to_delete); - nb_elements_until_last--; - } - - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - return to_delete; - } - - - - size_type erase(const CharT* key, size_type key_size) { - return erase(key, key_size, hash_key(key, key_size)); - } - - size_type erase(const CharT* key, size_type key_size, std::size_t hash) { - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - const std::size_t ibucket = bucket_for_hash(hash); - if(m_buckets[ibucket].erase(key, key_size)) { - m_nb_elements--; - return 1; - } - else { - return 0; - } - } - - - - void swap(array_hash& other) { - using std::swap; - - swap(static_cast&>(*this), static_cast&>(other)); - swap(static_cast(*this), static_cast(other)); - swap(static_cast(*this), static_cast(other)); - swap(m_buckets_data, other.m_buckets_data); - swap(m_buckets, other.m_buckets); - swap(m_nb_elements, other.m_nb_elements); - swap(m_max_load_factor, other.m_max_load_factor); - swap(m_load_threshold, other.m_load_threshold); - } - - /* - * Lookup - */ - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size) { - return at(key, key_size, hash_key(key, key_size)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size) const { - return at(key, key_size, hash_key(key, key_size)); - } - - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size, std::size_t hash) { - return const_cast(static_cast(this)->at(key, key_size, hash)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return this->m_values[it_find.first.value()]; - } - else { - THROW(std::out_of_range, "Couldn't find key."); - } - } - - - - template::value>::type* = nullptr> - U& access_operator(const CharT* key, size_type key_size) { - const std::size_t hash = hash_key(key, key_size); - std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return this->m_values[it_find.first.value()]; - } - else { - if(grow_on_high_load()) { - ibucket = bucket_for_hash(hash); - it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - } - - return emplace_impl(ibucket, it_find.first, key, key_size, U{}).first.value(); - } - } - - - - size_type count(const CharT* key, size_type key_size) const { - return count(key, key_size, hash_key(key, key_size)); - } - - size_type count(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return 1; - } - else { - return 0; - } - } - - - - iterator find(const CharT* key, size_type key_size) { - return find(key, key_size, hash_key(key, key_size)); - } - - const_iterator find(const CharT* key, size_type key_size) const { - return find(key, key_size, hash_key(key, key_size)); - } - - iterator find(const CharT* key, size_type key_size, std::size_t hash) { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return iterator(m_buckets_data.begin() + ibucket, it_find.first, this); - } - else { - return end(); - } - } - - const_iterator find(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return const_iterator(m_buckets_data.cbegin() + ibucket, it_find.first, this); - } - else { - return cend(); - } - } - - - - std::pair equal_range(const CharT* key, size_type key_size) { - return equal_range(key, key_size, hash_key(key, key_size)); - } - - std::pair equal_range(const CharT* key, size_type key_size) const { - return equal_range(key, key_size, hash_key(key, key_size)); - } - - std::pair equal_range(const CharT* key, size_type key_size, std::size_t hash) { - iterator it = find(key, key_size, hash); - return std::make_pair(it, (it == end())?it:std::next(it)); - } - - std::pair equal_range(const CharT* key, size_type key_size, - std::size_t hash) const - { - const_iterator it = find(key, key_size, hash); - return std::make_pair(it, (it == cend())?it:std::next(it)); - } - - /* - * Bucket interface - */ - size_type bucket_count() const { - return m_buckets_data.size(); - } - - size_type max_bucket_count() const { - return std::min(GrowthPolicy::max_bucket_count(), m_buckets_data.max_size()); - } - - - /* - * Hash policy - */ - float load_factor() const { - if(bucket_count() == 0) { - return 0; - } - - return float(m_nb_elements) / float(bucket_count()); - } - - float max_load_factor() const { - return m_max_load_factor; - } - - void max_load_factor(float ml) { - m_max_load_factor = std::max(0.1f, ml); - m_load_threshold = size_type(float(bucket_count())*m_max_load_factor); - } - - void rehash(size_type count) { - count = std::max(count, size_type(std::ceil(float(size())/max_load_factor()))); - rehash_impl(count); - } - - void reserve(size_type count) { - rehash(size_type(std::ceil(float(count)/max_load_factor()))); - } - - /* - * Observers - */ - hasher hash_function() const { - return static_cast(*this); - } - - // TODO add support for statefull KeyEqual - key_equal key_eq() const { - return KeyEqual(); - } - - /* - * Other - */ - iterator mutable_iterator(const_iterator it) noexcept { - auto it_bucket = m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), it.m_buckets_iterator); - return iterator(it_bucket, it.m_array_bucket_iterator, this); - } - - template - void serialize(Serializer& serializer) const { - serialize_impl(serializer); - } - - template - void deserialize(Deserializer& deserializer, bool hash_compatible) { - deserialize_impl(deserializer, hash_compatible); - } - -private: - std::size_t hash_key(const CharT* key, size_type key_size) const { - return Hash::operator()(key, key_size); - } - - std::size_t bucket_for_hash(std::size_t hash) const { - return GrowthPolicy::bucket_for_hash(hash); - } - - /** - * If there is a mapped_type, the mapped value in m_values is not erased now. - * It will be erased when the ratio between the size of the map and - * the size of the map + the number of deleted values still stored is low enough (see clear_old_erased_values). - */ - iterator erase_from_bucket(iterator pos) noexcept { - auto array_bucket_next_it = pos.m_buckets_iterator->erase(pos.m_array_bucket_iterator); - m_nb_elements--; - - if(array_bucket_next_it != pos.m_buckets_iterator->cend()) { - return iterator(pos.m_buckets_iterator, array_bucket_next_it, this); - } - else { - do { - ++pos.m_buckets_iterator; - } while(pos.m_buckets_iterator != m_buckets_data.end() && pos.m_buckets_iterator->empty()); - - if(pos.m_buckets_iterator != m_buckets_data.end()) { - return iterator(pos.m_buckets_iterator, pos.m_buckets_iterator->cbegin(), this); - } - else { - return end(); - } - } - } - - - template::value>::type* = nullptr> - bool should_clear_old_erased_values(float /*threshold*/ = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD) const { - return false; - } - - template::value>::type* = nullptr> - bool should_clear_old_erased_values(float threshold = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD) const { - if(this->m_values.size() == 0) { - return false; - } - - return float(m_nb_elements)/float(this->m_values.size()) < threshold; - } - - template::value>::type* = nullptr> - void clear_old_erased_values() { - } - - template::value>::type* = nullptr> - void clear_old_erased_values() { - static_assert(std::is_nothrow_move_constructible::value || - std::is_copy_constructible::value, - "mapped_value must be either copy constructible or nothrow move constructible."); - - if(m_nb_elements == this->m_values.size()) { - return; - } - - std::vector new_values; - new_values.reserve(size()); - - for(auto it = begin(); it != end(); ++it) { - new_values.push_back(std::move_if_noexcept(it.value())); - } - - - IndexSizeT ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - auto it_array_bucket = it.m_buckets_iterator->mutable_iterator(it.m_array_bucket_iterator); - it_array_bucket.set_value(ivalue); - ivalue++; - } - - new_values.swap(this->m_values); - tsl_ah_assert(m_nb_elements == this->m_values.size()); - } - - /** - * Return true if a rehash occurred. - */ - bool grow_on_high_load() { - if(size() >= m_load_threshold) { - rehash_impl(GrowthPolicy::next_bucket_count()); - return true; - } - - return false; - } - - template::value>::type* = nullptr> - std::pair emplace_impl(std::size_t ibucket, typename array_bucket::const_iterator end_of_bucket, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - if(this->m_values.size() >= max_size()) { - // Try to clear old erased values lingering in m_values. Throw if it doesn't change anything. - clear_old_erased_values(); - if(this->m_values.size() >= max_size()) { - THROW(std::length_error, "Can't insert value, too much values in the map."); - } - } - - if(this->m_values.size() == this->m_values.capacity()) { - this->m_values.reserve(std::size_t(float(this->m_values.size()) * value_container::VECTOR_GROWTH_RATE)); - } - - - this->m_values.emplace_back(std::forward(value_args)...); - - auto it = m_buckets[ibucket].append(end_of_bucket, key, key_size, IndexSizeT(this->m_values.size() - 1)); - m_nb_elements++; - - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it, this), true); - } - - template::value>::type* = nullptr> - std::pair emplace_impl(std::size_t ibucket, typename array_bucket::const_iterator end_of_bucket, - const CharT* key, size_type key_size) - { - if(m_nb_elements >= max_size()) { - THROW(std::length_error, "Can't insert value, too much values in the map."); - } - - auto it = m_buckets[ibucket].append(end_of_bucket, key, key_size); - m_nb_elements++; - - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it, this), true); - } - - void rehash_impl(size_type bucket_count) { - GrowthPolicy new_growth_policy(bucket_count); - if(bucket_count == this->bucket_count()) { - return; - } - - - if(should_clear_old_erased_values(REHASH_CLEAR_OLD_ERASED_VALUE_THRESHOLD)) { - clear_old_erased_values(); - } - - - std::vector required_size_for_bucket(bucket_count, 0); - std::vector bucket_for_ivalue(size(), 0); - - std::size_t ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - const std::size_t hash = hash_key(it.key(), it.key_size()); - const std::size_t ibucket = new_growth_policy.bucket_for_hash(hash); - - bucket_for_ivalue[ivalue] = ibucket; - required_size_for_bucket[ibucket] += array_bucket::entry_required_bytes(it.key_size()); - ivalue++; - } - - - - - std::vector new_buckets; - new_buckets.reserve(bucket_count); - for(std::size_t ibucket = 0; ibucket < bucket_count; ibucket++) { - new_buckets.emplace_back(required_size_for_bucket[ibucket]); - } - - - ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - const std::size_t ibucket = bucket_for_ivalue[ivalue]; - append_iterator_in_reserved_bucket_no_check(new_buckets[ibucket], it); - - ivalue++; - } - - - using std::swap; - swap(static_cast(*this), new_growth_policy); - - m_buckets_data.swap(new_buckets); - m_buckets = !m_buckets_data.empty()?m_buckets_data.data(): - static_empty_bucket_ptr(); - - // Call max_load_factor to change m_load_threshold - max_load_factor(m_max_load_factor); - } - - template::value>::type* = nullptr> - void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) { - bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size()); - } - - template::value>::type* = nullptr> - void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) { - bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size(), it.value_position()); - } - - - - /** - * On serialization the values of each bucket (if has_mapped_type is true) are serialized - * next to the bucket. The potential old erased values in value_container are thus not serialized. - * - * On deserialization, when hash_compatible is true, we reaffect the value index (IndexSizeT) of each - * bucket with set_value as the position of each value is no more the same in value_container compared - * to when they were serialized. - * - * It's done this way as we can't call clear_old_erased_values() because we want the serialize - * method to remain const and we don't want to serialize/deserialize old erased values. As we may - * not serialize all the values in value_container, the values we keep can change of index. - * We thus have to modify the value indexes in the buckets. - */ - template - void serialize_impl(Serializer& serializer) const { - const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; - serializer(version); - - const slz_size_type bucket_count = m_buckets_data.size(); - serializer(bucket_count); - - const slz_size_type nb_elements = m_nb_elements; - serializer(nb_elements); - - const float max_load_factor = m_max_load_factor; - serializer(max_load_factor); - - for(const array_bucket& bucket: m_buckets_data) { - bucket.serialize(serializer); - serialize_bucket_values(serializer, bucket); - } - } - - template::value>::type* = nullptr> - void serialize_bucket_values(Serializer& /*serializer*/, const array_bucket& /*bucket*/) const { - } - - template::value>::type* = nullptr> - void serialize_bucket_values(Serializer& serializer, const array_bucket& bucket) const { - for(auto it = bucket.begin(); it != bucket.end(); ++it) { - serializer(this->m_values[it.value()]); - } - } - - template - void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { - tsl_ah_assert(m_buckets_data.empty()); // Current hash table must be empty - - const slz_size_type version = deserialize_value(deserializer); - // For now we only have one version of the serialization protocol. - // If it doesn't match there is a problem with the file. - if(version != SERIALIZATION_PROTOCOL_VERSION) { - THROW(std::runtime_error, "Can't deserialize the array_map/set. The protocol version header is invalid."); - } - - const slz_size_type bucket_count_ds = deserialize_value(deserializer); - const slz_size_type nb_elements = deserialize_value(deserializer); - const float max_load_factor = deserialize_value(deserializer); - - - m_nb_elements = numeric_cast(nb_elements, "Deserialized nb_elements is too big."); - - size_type bucket_count = numeric_cast(bucket_count_ds, "Deserialized bucket_count is too big."); - GrowthPolicy::operator=(GrowthPolicy(bucket_count)); - - - this->max_load_factor(max_load_factor); - value_container::reserve(m_nb_elements); - - - if(hash_compatible) { - if(bucket_count != bucket_count_ds) { - THROW(std::runtime_error, "The GrowthPolicy is not the same even though hash_compatible is true."); - } - - m_buckets_data.reserve(bucket_count); - for(size_type i = 0; i < bucket_count; i++) { - m_buckets_data.push_back(array_bucket::deserialize(deserializer)); - deserialize_bucket_values(deserializer, m_buckets_data.back()); - } - } - else { - m_buckets_data.resize(bucket_count); - for(size_type i = 0; i < bucket_count; i++) { - // TODO use buffer to avoid reallocation on each deserialization. - array_bucket bucket = array_bucket::deserialize(deserializer); - deserialize_bucket_values(deserializer, bucket); - - for(auto it_val = bucket.cbegin(); it_val != bucket.cend(); ++it_val) { - const std::size_t ibucket = bucket_for_hash(hash_key(it_val.key(), it_val.key_size())); - - auto it_find = m_buckets_data[ibucket].find_or_end_of_bucket(it_val.key(), it_val.key_size()); - if(it_find.second) { - THROW(std::runtime_error, "Error on deserialization, the same key is presents multiple times."); - } - - append_array_bucket_iterator_in_bucket(m_buckets_data[ibucket], it_find.first, it_val); - } - } - } - - m_buckets = m_buckets_data.data(); - - - if(load_factor() > this->max_load_factor()) { - THROW(std::runtime_error, "Invalid max_load_factor. Check that the serializer and deserializer support " - "floats correctly as they can be converted implicitely to ints."); - } - } - - template::value>::type* = nullptr> - void deserialize_bucket_values(Deserializer& /*deserializer*/, array_bucket& /*bucket*/) { - } - - template::value>::type* = nullptr> - void deserialize_bucket_values(Deserializer& deserializer, array_bucket& bucket) { - for(auto it = bucket.begin(); it != bucket.end(); ++it) { - this->m_values.emplace_back(deserialize_value(deserializer)); - - tsl_ah_assert(this->m_values.size() - 1 <= std::numeric_limits::max()); - it.set_value(static_cast(this->m_values.size() - 1)); - } - } - - template::value>::type* = nullptr> - void append_array_bucket_iterator_in_bucket(array_bucket& bucket, - typename array_bucket::const_iterator end_of_bucket, - typename array_bucket::const_iterator it_val) - { - bucket.append(end_of_bucket, it_val.key(), it_val.key_size()); - } - - template::value>::type* = nullptr> - void append_array_bucket_iterator_in_bucket(array_bucket& bucket, - typename array_bucket::const_iterator end_of_bucket, - typename array_bucket::const_iterator it_val) - { - bucket.append(end_of_bucket, it_val.key(), it_val.key_size(), it_val.value()); - } - -public: - static const size_type DEFAULT_INIT_BUCKET_COUNT = 0; - static constexpr float DEFAULT_MAX_LOAD_FACTOR = 2.0f; - static const size_type MAX_KEY_SIZE = array_bucket::MAX_KEY_SIZE; - -private: - /** - * Protocol version currenlty used for serialization. - */ - static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; - - - static constexpr float DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD = 0.6f; - static constexpr float REHASH_CLEAR_OLD_ERASED_VALUE_THRESHOLD = 0.9f; - - - /** - * Return an always valid pointer to a static empty array_bucket. - */ - array_bucket* static_empty_bucket_ptr() { - static array_bucket empty_bucket; - return &empty_bucket; - } - -private: - std::vector m_buckets_data; - - /** - * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points to static_empty_bucket_ptr. - * This variable is useful to avoid the cost of checking if m_buckets_data is empty when trying - * to find an element. - * - * TODO Remove m_buckets_data and only use a pointer+size instead of a pointer+vector to save some space in the array_hash object. - */ - array_bucket* m_buckets; - - IndexSizeT m_nb_elements; - float m_max_load_factor; - size_type m_load_threshold; -}; - -} // end namespace detail_array_hash -} //end namespace tsl - -#endif diff --git a/ios/include/trie/array-hash/array_map.h b/ios/include/trie/array-hash/array_map.h deleted file mode 100644 index bc534bf2..00000000 --- a/ios/include/trie/array-hash/array_map.h +++ /dev/null @@ -1,863 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_MAP_H -#define TSL_ARRAY_MAP_H - -#include -#include -#include -#include -#include -#include -#include -#include "array_hash.h" - -namespace tsl { - - -/** - * Implementation of a cache-conscious string hash map. - * - * The map stores the strings as `const CharT*`. If `StoreNullTerminator` is true, - * the strings are stored with the a null-terminator (the `key()` method of the iterators - * will return a pointer to this null-terminated string). Otherwise the null character - * is not stored (which allow an economy of 1 byte per string). - * - * The value `T` must be either nothrow move-constructible, copy-constructible or both. - * - * The size of a key string is limited to `std::numeric_limits::max() - 1`. - * That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter. - * See `max_key_size()` for an easy access to this limit. - * - * The number of elements in the map is limited to `std::numeric_limits::max()`. - * That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter. - * See `max_size()` for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - * - shrink_to_fit: always invalidate the iterators. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t, - class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>> -class array_map { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_array_hash::array_hash; - -public: - using char_type = typename ht::char_type; - using mapped_type = T; - using key_size_type = typename ht::key_size_type; - using index_size_type = typename ht::index_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using key_equal = typename ht::key_equal; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - -public: - array_map(): array_map(ht::DEFAULT_INIT_BUCKET_COUNT) { - } - - explicit array_map(size_type bucket_count, - const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR) - { - } - - template::value>::type* = nullptr> - array_map(InputIt first, InputIt last, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(first, last); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_map(std::initializer_list, T>> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(init); - } -#else - array_map(std::initializer_list> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_map& operator=(std::initializer_list, T>> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#else - array_map& operator=(std::initializer_list> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, const T& value) { - return m_ht.emplace(key.data(), key.size(), value); - } -#else - std::pair insert(const CharT* key, const T& value) { - return m_ht.emplace(key, std::char_traits::length(key), value); - } - - std::pair insert(const std::basic_string& key, const T& value) { - return m_ht.emplace(key.data(), key.size(), value); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size, const T& value) { - return m_ht.emplace(key, key_size, value); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, T&& value) { - return m_ht.emplace(key.data(), key.size(), std::move(value)); - } -#else - std::pair insert(const CharT* key, T&& value) { - return m_ht.emplace(key, std::char_traits::length(key), std::move(value)); - } - - std::pair insert(const std::basic_string& key, T&& value) { - return m_ht.emplace(key.data(), key.size(), std::move(value)); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size, T&& value) { - return m_ht.emplace(key, key_size, std::move(value)); - } - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - if(std::is_base_of::iterator_category>::value) - { - const auto nb_elements_insert = std::distance(first, last); - const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size(); - - if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) { - reserve(size() + std::size_t(nb_elements_insert)); - } - } - - for(auto it = first; it != last; ++it) { - insert_pair(*it); - } - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - void insert(std::initializer_list, T>> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - template - std::pair insert_or_assign(const std::basic_string_view& key, M&& obj) { - return m_ht.insert_or_assign(key.data(), key.size(), std::forward(obj)); - } -#else - template - std::pair insert_or_assign(const CharT* key, M&& obj) { - return m_ht.insert_or_assign(key, std::char_traits::length(key), std::forward(obj)); - } - - template - std::pair insert_or_assign(const std::basic_string& key, M&& obj) { - return m_ht.insert_or_assign(key.data(), key.size(), std::forward(obj)); - } -#endif - template - std::pair insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) { - return m_ht.insert_or_assign(key, key_size, std::forward(obj)); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - template - std::pair emplace(const std::basic_string_view& key, Args&&... args) { - return m_ht.emplace(key.data(), key.size(), std::forward(args)...); - } -#else - template - std::pair emplace(const CharT* key, Args&&... args) { - return m_ht.emplace(key, std::char_traits::length(key), std::forward(args)...); - } - - template - std::pair emplace(const std::basic_string& key, Args&&... args) { - return m_ht.emplace(key.data(), key.size(), std::forward(args)...); - } -#endif - template - std::pair emplace_ks(const CharT* key, size_type key_size, Args&&... args) { - return m_ht.emplace(key, key_size, std::forward(args)...); - } - - - - /** - * Erase has an amortized O(1) runtime complexity, but even if it removes the key immediately, - * it doesn't do the same for the associated value T. - * - * T will only be removed when the ratio between the size of the map and - * the size of the map + the number of deleted values still stored is low enough. - * - * To force the deletion you can call shrink_to_fit. - */ - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - - /** - * @copydoc erase(const_iterator pos) - */ - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const CharT* key) { - return m_ht.erase(key, std::char_traits::length(key)); - } - - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const CharT* key, std::size_t precalculated_hash) { - return m_ht.erase(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * @copydoc erase(const_iterator pos) - * - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.erase(key, key_size, precalculated_hash); - } - - - - void swap(array_map& other) { other.m_ht.swap(m_ht); } - - - - /* - * Lookup - */ -#ifdef TSL_AH_HAS_STRING_VIEW - T& at(const std::basic_string_view& key) { - return m_ht.at(key.data(), key.size()); - } - - const T& at(const std::basic_string_view& key) const { - return m_ht.at(key.data(), key.size()); - } -#else - T& at(const CharT* key) { - return m_ht.at(key, std::char_traits::length(key)); - } - - const T& at(const CharT* key) const { - return m_ht.at(key, std::char_traits::length(key)); - } - - T& at(const std::basic_string& key) { - return m_ht.at(key.data(), key.size()); - } - - const T& at(const std::basic_string& key) const { - return m_ht.at(key.data(), key.size()); - } -#endif - T& at_ks(const CharT* key, size_type key_size) { - return m_ht.at(key, key_size); - } - - const T& at_ks(const CharT* key, size_type key_size) const { - return m_ht.at(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const CharT* key, std::size_t precalculated_hash) { - return m_ht.at(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.at(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.at(key, key_size, precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.at(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - T& operator[](const std::basic_string_view& key) { return m_ht.access_operator(key.data(), key.size()); } -#else - T& operator[](const CharT* key) { return m_ht.access_operator(key, std::char_traits::length(key)); } - T& operator[](const std::basic_string& key) { return m_ht.access_operator(key.data(), key.size()); } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { - return m_ht.count(key.data(), key.size()); - } -#else - size_type count(const CharT* key) const { - return m_ht.count(key, std::char_traits::length(key)); - } - - size_type count(const std::basic_string& key) const { - return m_ht.count(key.data(), key.size()); - } -#endif - size_type count_ks(const CharT* key, size_type key_size) const { - return m_ht.count(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.count(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.count(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::char_traits::length(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::char_traits::length(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const CharT* key, std::size_t precalculated_hash) { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.find(key, key_size, precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.find(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - - - /* - * Bucket interface - */ - size_type bucket_count() const { return m_ht.bucket_count(); } - size_type max_bucket_count() const { return m_ht.max_bucket_count(); } - - - /* - * Hash policy - */ - float load_factor() const { return m_ht.load_factor(); } - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - void rehash(size_type count) { m_ht.rehash(count); } - void reserve(size_type count) { m_ht.reserve(count); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - key_equal key_eq() const { return m_ht.key_eq(); } - - - /* - * Other - */ - /** - * Return the `const_iterator it` as an `iterator`. - */ - iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); } - - /** - * Serialize the map through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `template void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - /** - * Deserialize a previously serialized map through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be - * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the - * serialized map. Otherwise the behaviour is undefined with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` and `T` of the `array_map` are not the same as the - * types used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static array_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { - array_map map(0); - map.m_ht.deserialize(deserializer, hash_compatible); - - return map; - } - - friend bool operator==(const array_map& lhs, const array_map& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size()); - if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const array_map& lhs, const array_map& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(array_map& lhs, array_map& rhs) { - lhs.swap(rhs); - } - -private: - template - void insert_pair(const std::pair& value) { - insert(value.first, value.second); - } - - template - void insert_pair(std::pair&& value) { - insert(value.first, std::move(value.second)); - } - -public: - static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE; - -private: - ht m_ht; -}; - - -/** - * Same as - * `tsl::array_map`. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t> -using array_pg_map = array_map; - -} //end namespace tsl - -#endif diff --git a/ios/include/trie/array-hash/array_set.h b/ios/include/trie/array-hash/array_set.h deleted file mode 100644 index 0322bcd0..00000000 --- a/ios/include/trie/array-hash/array_set.h +++ /dev/null @@ -1,664 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_SET_H -#define TSL_ARRAY_SET_H - -#include -#include -#include -#include -#include -#include -#include -#include "array_hash.h" - -namespace tsl { - -/** - * Implementation of a cache-conscious string hash set. - * - * The set stores the strings as `const CharT*`. If `StoreNullTerminator` is true, - * the strings are stored with the a null-terminator (the `key()` method of the iterators - * will return a pointer to this null-terminated string). Otherwise the null character - * is not stored (which allow an economy of 1 byte per string). - * - * The size of a key string is limited to `std::numeric_limits::max() - 1`. - * That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter. - * See `max_key_size()` for an easy access to this limit. - * - * The number of elements in the set is limited to `std::numeric_limits::max()`. - * That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter. - * See `max_size()` for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - * - shrink_to_fit: always invalidate the iterators. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t, - class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>> -class array_set { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_array_hash::array_hash; - -public: - using char_type = typename ht::char_type; - using key_size_type = typename ht::key_size_type; - using index_size_type = typename ht::index_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using key_equal = typename ht::key_equal; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - - array_set(): array_set(ht::DEFAULT_INIT_BUCKET_COUNT) { - } - - explicit array_set(size_type bucket_count, - const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR) - { - } - - template::value>::type* = nullptr> - array_set(InputIt first, InputIt last, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(first, last); - } - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_set(std::initializer_list> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(init); - } -#else - array_set(std::initializer_list init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_set& operator=(std::initializer_list> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#else - array_set& operator=(std::initializer_list ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#endif - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key) { - return m_ht.emplace(key.data(), key.size()); - } -#else - std::pair insert(const CharT* key) { - return m_ht.emplace(key, std::char_traits::length(key)); - } - - std::pair insert(const std::basic_string& key) { - return m_ht.emplace(key.data(), key.size()); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size) { - return m_ht.emplace(key, key_size); - } - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - if(std::is_base_of::iterator_category>::value) - { - const auto nb_elements_insert = std::distance(first, last); - const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size(); - - if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) { - reserve(size() + std::size_t(nb_elements_insert)); - } - } - - for(auto it = first; it != last; ++it) { - insert(*it); - } - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const std::basic_string_view& key) { - return m_ht.emplace(key.data(), key.size()); - } -#else - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const CharT* key) { - return m_ht.emplace(key, std::char_traits::length(key)); - } - - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const std::basic_string& key) { - return m_ht.emplace(key.data(), key.size()); - } -#endif - /** - * No difference compared to the insert method. Mainly here for coherence with array_map. - */ - std::pair emplace_ks(const CharT* key, size_type key_size) { - return m_ht.emplace(key, key_size); - } - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - -#ifdef TSL_AH_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::char_traits::length(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const CharT* key, std::size_t precalculated_hash) { - return m_ht.erase(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.erase(key, key_size, precalculated_hash); - } - - - - void swap(array_set& other) { other.m_ht.swap(m_ht); } - - - - /* - * Lookup - */ -#ifdef TSL_AH_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::char_traits::length(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.count(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.count(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::char_traits::length(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::char_traits::length(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const CharT* key, std::size_t precalculated_hash) { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.find(key, key_size, precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.find(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - - - /* - * Bucket interface - */ - size_type bucket_count() const { return m_ht.bucket_count(); } - size_type max_bucket_count() const { return m_ht.max_bucket_count(); } - - - /* - * Hash policy - */ - float load_factor() const { return m_ht.load_factor(); } - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - void rehash(size_type count) { m_ht.rehash(count); } - void reserve(size_type count) { m_ht.reserve(count); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - key_equal key_eq() const { return m_ht.key_eq(); } - - - /* - * Other - */ - /** - * Return the `const_iterator it` as an `iterator`. - */ - iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); } - - /** - * Serialize the set through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `template void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - /** - * Deserialize a previously serialized set through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be - * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the - * serialized set. Otherwise the behaviour is undefined with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` of the `array_set` is not the same as the - * type used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static array_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { - array_set set(0); - set.m_ht.deserialize(deserializer, hash_compatible); - - return set; - } - - friend bool operator==(const array_set& lhs, const array_set& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size()); - if(it_element_rhs == rhs.cend()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const array_set& lhs, const array_set& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(array_set& lhs, array_set& rhs) { - lhs.swap(rhs); - } - -public: - static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE; - -private: - ht m_ht; -}; - - -/** - * Same as - * `tsl::array_set`. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t> -using array_pg_set = array_set; - -} //end namespace tsl - -#endif diff --git a/ios/include/trie/htrie_hash.h b/ios/include/trie/htrie_hash.h deleted file mode 100644 index 99ee0724..00000000 --- a/ios/include/trie/htrie_hash.h +++ /dev/null @@ -1,2090 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_HASH_H -#define TSL_HTRIE_HASH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array-hash/array_map.h" -#include "array-hash/array_set.h" - - -/* - * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433), - * check also __cplusplus version. - */ -#ifdef __has_include -# if __has_include() && __cplusplus >= 201703L -# define TSL_HT_HAS_STRING_VIEW -# endif -#endif - - -#ifdef TSL_HT_HAS_STRING_VIEW -# include -#endif - - -#ifdef TSL_DEBUG -# define tsl_ht_assert(expr) assert(expr) -#else -# define tsl_ht_assert(expr) (static_cast(0)) -#endif - - -namespace tsl { - -namespace detail_htrie_hash { - -template -struct is_iterator: std::false_type { -}; - -template -struct is_iterator::iterator_category, void>::value>::type>: std::true_type { -}; - -template -struct is_related: std::false_type {}; - -template -struct is_related: std::is_same::type>::type, - typename std::remove_cv::type>::type> {}; - -template -static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { - T ret = static_cast(value); - if(static_cast(ret) != value) { - THROW(std::runtime_error, error_message); - } - - const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || - (std::is_signed::value && std::is_signed::value); - if(!is_same_signedness && (ret < T{}) != (value < U{})) { - THROW(std::runtime_error, error_message); - } - - return ret; -} - - -template -struct value_node { - /* - * Avoid conflict with copy constructor 'value_node(const value_node&)'. If we call the copy constructor - * with a mutable reference 'value_node(value_node&)', we don't want the forward constructor to be called. - */ - template::value>::type* = nullptr> - value_node(Args&&... args): m_value(std::forward(args)...) { - } - - T m_value; -}; - -template<> -struct value_node { -}; - - -/** - * T should be void if there is no value associated to a key (in a set for example). - */ -template -class htrie_hash { -private: - template - using has_value = typename std::integral_constant::value>; - - static_assert(std::is_same::value, "char is the only supported CharT type for now."); - - static const std::size_t ALPHABET_SIZE = - std::numeric_limits::type>::max() + 1; - - -public: - template - class htrie_hash_iterator; - - - using char_type = CharT; - using key_size_type = KeySizeT; - using size_type = std::size_t; - using hasher = Hash; - using iterator = htrie_hash_iterator; - using const_iterator = htrie_hash_iterator; - using prefix_iterator = htrie_hash_iterator; - using const_prefix_iterator = htrie_hash_iterator; - - -private: - using array_hash_type = - typename std::conditional< - has_value::value, - tsl::array_map, false, - KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>, - tsl::array_set, false, - KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>>::type; - - -private: - /* - * The tree is mainly composed of two nodes types: trie_node and hash_node which both have anode as base class. - * Each child is either a hash_node or a trie_node. - * - * A hash_node is always a leaf node, it doesn't have any child. - * - * Example: - * | ... | a |.. ..................... | f | ... | trie_node_1 - * \ \ - * hash_node_1 |array_hash = {"dd"}| |...| a | ... | trie_node_2 - * / - * |array_hash = {"ble", "bric", "lse"}| hash_node_2 - * - * - * Each trie_node may also have a value node, which contains a value T, if the trie_node marks - * the end of a string value. - * - * A trie node should at least have one child or a value node. There can't be a trie node without - * any child and no value node. - */ - - using value_node = tsl::detail_htrie_hash::value_node; - - - class trie_node; - class hash_node; - - // TODO better encapsulate operations modifying the tree. - class anode { - friend class trie_node; - - public: - /* - * TODO Avoid the virtual to economize 8 bytes. We could use a custom deleter in the std::unique_ptr - * we use (as we know if an anode is a trie_node or hash_node). - */ - virtual ~anode() = default; - - bool is_trie_node() const noexcept { - return m_node_type == node_type::TRIE_NODE; - } - - bool is_hash_node() const noexcept { - return m_node_type == node_type::HASH_NODE; - } - - trie_node& as_trie_node() noexcept { - tsl_ht_assert(is_trie_node()); - return static_cast(*this); - } - - hash_node& as_hash_node() noexcept { - tsl_ht_assert(is_hash_node()); - return static_cast(*this); - } - - const trie_node& as_trie_node() const noexcept { - tsl_ht_assert(is_trie_node()); - return static_cast(*this); - } - - const hash_node& as_hash_node() const noexcept { - tsl_ht_assert(is_hash_node()); - return static_cast(*this); - } - - /** - * @see m_child_of_char - */ - CharT child_of_char() const noexcept { - tsl_ht_assert(parent() != nullptr); - return m_child_of_char; - } - - /** - * Return nullptr if none. - */ - trie_node* parent() noexcept { - return m_parent_node; - } - - const trie_node* parent() const noexcept { - return m_parent_node; - } - - protected: - enum class node_type: unsigned char { - HASH_NODE, - TRIE_NODE - }; - - anode(node_type node_type_): m_node_type(node_type_), m_child_of_char(0), - m_parent_node(nullptr) - { - } - - anode(node_type node_type_, CharT child_of_char): m_node_type(node_type_), - m_child_of_char(child_of_char), - m_parent_node(nullptr) - { - } - - - protected: - node_type m_node_type; - - /** - * If the node has a parent, then it's a descendant of some char. - * - * Example: - * | ... | a | b | ... | trie_node_1 - * \ - * |...| a | ... | trie_node_2 - * / - * |array_hash| hash_node_1 - * - * trie_node_2 is a child of trie_node_1 through 'b', it will have 'b' as m_child_of_char. - * hash_node_1 is a child of trie_node_2 through 'a', it will have 'a' as m_child_of_char. - * - * trie_node_1 has no parent, its m_child_of_char is undefined. - */ - CharT m_child_of_char; - trie_node* m_parent_node; - }; - - // Give the position in trie_node::m_children corresponding to the character c - static std::size_t as_position(CharT c) noexcept { - return static_cast(static_cast::type>(c)); - } - - class trie_node: public anode { - public: - trie_node(): anode(anode::node_type::TRIE_NODE), - m_value_node(nullptr), m_children() - { - } - - trie_node(const trie_node& other): anode(anode::node_type::TRIE_NODE, other.m_child_of_char), - m_value_node(nullptr), m_children() - { - if(other.m_value_node != nullptr) { - m_value_node = make_unique(*other.m_value_node); - } - - // TODO avoid recursion - for(std::size_t ichild = 0; ichild < other.m_children.size(); ichild++) { - if(other.m_children[ichild] != nullptr) { - if(other.m_children[ichild]->is_hash_node()) { - m_children[ichild] = make_unique(other.m_children[ichild]->as_hash_node()); - } - else { - m_children[ichild] = make_unique(other.m_children[ichild]->as_trie_node()); - } - - m_children[ichild]->m_parent_node = this; - } - } - } - - trie_node(trie_node&& other) = delete; - trie_node& operator=(const trie_node& other) = delete; - trie_node& operator=(trie_node&& other) = delete; - - /** - * Return nullptr if none. - */ - anode* first_child() noexcept { - return const_cast(static_cast(this)->first_child()); - } - - const anode* first_child() const noexcept { - for(std::size_t ichild = 0; ichild < m_children.size(); ichild++) { - if(m_children[ichild] != nullptr) { - return m_children[ichild].get(); - } - } - - return nullptr; - } - - - /** - * Get the next_child that come after current_child. Return nullptr if no next child. - */ - anode* next_child(const anode& current_child) noexcept { - return const_cast(static_cast(this)->next_child(current_child)); - } - - const anode* next_child(const anode& current_child) const noexcept { - tsl_ht_assert(current_child.parent() == this); - - for(std::size_t ichild = as_position(current_child.child_of_char()) + 1; - ichild < m_children.size(); - ichild++) - { - if(m_children[ichild] != nullptr) { - return m_children[ichild].get(); - } - } - - return nullptr; - } - - - /** - * Return the first left-descendant trie node with an m_value_node. If none return the most left trie node. - */ - trie_node& most_left_descendant_value_trie_node() noexcept { - return const_cast(static_cast(this)->most_left_descendant_value_trie_node()); - } - - const trie_node& most_left_descendant_value_trie_node() const noexcept { - const trie_node* current_node = this; - while(true) { - if(current_node->m_value_node != nullptr) { - return *current_node; - } - - const anode* first_child = current_node->first_child(); - tsl_ht_assert(first_child != nullptr); // a trie_node must either have a value_node or at least one child. - if(first_child->is_hash_node()) { - return *current_node; - } - - current_node = &first_child->as_trie_node(); - } - } - - - - size_type nb_children() const noexcept { - return std::count_if(m_children.cbegin(), m_children.cend(), - [](const std::unique_ptr& n) { return n != nullptr; }); - } - - bool empty() const noexcept { - return std::all_of(m_children.cbegin(), m_children.cend(), - [](const std::unique_ptr& n) { return n == nullptr; }); - } - - std::unique_ptr& child(CharT for_char) noexcept { - return m_children[as_position(for_char)]; - } - - const std::unique_ptr& child(CharT for_char) const noexcept { - return m_children[as_position(for_char)]; - } - - typename std::array, ALPHABET_SIZE>::iterator begin() noexcept { - return m_children.begin(); - } - - typename std::array, ALPHABET_SIZE>::iterator end() noexcept { - return m_children.end(); - } - - void set_child(CharT for_char, std::unique_ptr child) noexcept { - if(child != nullptr) { - child->m_child_of_char = for_char; - child->m_parent_node = this; - } - - m_children[as_position(for_char)] = std::move(child); - } - - std::unique_ptr& val_node() noexcept { - return m_value_node; - } - - const std::unique_ptr& val_node() const noexcept { - return m_value_node; - } - - private: - // TODO Avoid storing a value_node when has_value::value is false - std::unique_ptr m_value_node; - - /** - * Each character CharT corresponds to one position in the array. To convert a character - * to a position use the as_position method. - * - * TODO Try to reduce the size of m_children with a hash map, linear/binary search on array, ... - * TODO Store number of non-null values in m_children. Check if we can store this value in the alignment - * space as we don't want the node to get bigger (empty() and nb_children() are rarely used so it is - * not an important variable). - */ - std::array, ALPHABET_SIZE> m_children; - }; - - - class hash_node: public anode { - public: - hash_node(const Hash& hash, float max_load_factor): - hash_node(HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT, hash, max_load_factor) - { - } - - hash_node(size_type bucket_count, const Hash& hash, float max_load_factor): - anode(anode::node_type::HASH_NODE), m_array_hash(bucket_count, hash) - { - m_array_hash.max_load_factor(max_load_factor); - } - - hash_node(array_hash_type&& array_hash) noexcept(std::is_nothrow_move_constructible::value): - anode(anode::node_type::HASH_NODE), m_array_hash(std::move(array_hash)) - { - } - - hash_node(const hash_node& other) = default; - - hash_node(hash_node&& other) = delete; - hash_node& operator=(const hash_node& other) = delete; - hash_node& operator=(hash_node&& other) = delete; - - - array_hash_type& array_hash() noexcept { - return m_array_hash; - } - - const array_hash_type& array_hash() const noexcept { - return m_array_hash; - } - - private: - array_hash_type m_array_hash; - }; - - - -public: - template - class htrie_hash_iterator { - friend class htrie_hash; - - private: - using anode_type = typename std::conditional::type; - using trie_node_type = typename std::conditional::type; - using hash_node_type = typename std::conditional::type; - - using array_hash_iterator_type = - typename std::conditional::type; - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = typename std::conditional::value, T, void>::type; - using difference_type = std::ptrdiff_t; - using reference = typename std::conditional< - has_value::value, - typename std::conditional::type, - typename std::add_lvalue_reference::type>::type, - void>::type; - using pointer = typename std::conditional< - has_value::value, - typename std::conditional::type, - void>::type; - - private: - /** - * Start reading from start_hash_node->array_hash().begin(). - */ - htrie_hash_iterator(hash_node_type& start_hash_node) noexcept: - htrie_hash_iterator(start_hash_node, start_hash_node.array_hash().begin()) - { - } - - /** - * Start reading from iterator begin in start_hash_node->array_hash(). - */ - htrie_hash_iterator(hash_node_type& start_hash_node, array_hash_iterator_type begin) noexcept: - m_current_trie_node(start_hash_node.parent()), m_current_hash_node(&start_hash_node), - m_array_hash_iterator(begin), - m_array_hash_end_iterator(start_hash_node.array_hash().end()), - m_read_trie_node_value(false) - { - tsl_ht_assert(!m_current_hash_node->array_hash().empty()); - } - - /** - * Start reading from the value in start_trie_node. start_trie_node->val_node() should be non-null. - */ - htrie_hash_iterator(trie_node_type& start_trie_node) noexcept: - m_current_trie_node(&start_trie_node), m_current_hash_node(nullptr), - m_read_trie_node_value(true) - { - tsl_ht_assert(m_current_trie_node->val_node() != nullptr); - } - - template::type* = nullptr> - htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, - array_hash_iterator_type begin, array_hash_iterator_type end, - bool read_trie_node_value) noexcept: - m_current_trie_node(tnode), m_current_hash_node(hnode), - m_array_hash_iterator(begin), m_array_hash_end_iterator(end), - m_read_trie_node_value(read_trie_node_value) - { - } - - template::type* = nullptr> - htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, - array_hash_iterator_type begin, array_hash_iterator_type end, - bool read_trie_node_value, std::basic_string prefix_filter) noexcept: - m_current_trie_node(tnode), m_current_hash_node(hnode), - m_array_hash_iterator(begin), m_array_hash_end_iterator(end), - m_read_trie_node_value(read_trie_node_value), m_prefix_filter(std::move(prefix_filter)) - { - } - - public: - htrie_hash_iterator() noexcept { - } - - // Copy constructor from iterator to const_iterator. - template::type* = nullptr> - htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: - m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), - m_array_hash_iterator(other.m_array_hash_iterator), - m_array_hash_end_iterator(other.m_array_hash_end_iterator), - m_read_trie_node_value(other.m_read_trie_node_value) - { - } - - // Copy constructor from iterator to const_iterator. - template::type* = nullptr> - htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: - m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), - m_array_hash_iterator(other.m_array_hash_iterator), - m_array_hash_end_iterator(other.m_array_hash_end_iterator), - m_read_trie_node_value(other.m_read_trie_node_value), m_prefix_filter(other.m_prefix_filter) - { - } - - htrie_hash_iterator(const htrie_hash_iterator& other) = default; - htrie_hash_iterator(htrie_hash_iterator&& other) = default; - htrie_hash_iterator& operator=(const htrie_hash_iterator& other) = default; - htrie_hash_iterator& operator=(htrie_hash_iterator&& other) = default; - - void key(std::basic_string& key_buffer_out) const { - key_buffer_out.clear(); - - trie_node_type* tnode = m_current_trie_node; - while(tnode != nullptr && tnode->parent() != nullptr) { - key_buffer_out.push_back(tnode->child_of_char()); - tnode = tnode->parent(); - } - - std::reverse(key_buffer_out.begin(), key_buffer_out.end()); - - if(!m_read_trie_node_value) { - tsl_ht_assert(m_current_hash_node != nullptr); - if(m_current_hash_node->parent() != nullptr) { - key_buffer_out.push_back(m_current_hash_node->child_of_char()); - } - - key_buffer_out.append(m_array_hash_iterator.key(), m_array_hash_iterator.key_size()); - } - } - - std::basic_string key() const { - std::basic_string key_buffer; - key(key_buffer); - - return key_buffer; - } - - template::value>::type* = nullptr> - reference value() const { - if(this->m_read_trie_node_value) { - tsl_ht_assert(this->m_current_trie_node != nullptr); - tsl_ht_assert(this->m_current_trie_node->val_node() != nullptr); - - return this->m_current_trie_node->val_node()->m_value; - } - else { - return this->m_array_hash_iterator.value(); - } - } - - template::value>::type* = nullptr> - reference operator*() const { - return value(); - } - - template::value>::type* = nullptr> - pointer operator->() const { - return std::addressof(value()); - } - - htrie_hash_iterator& operator++() { - if(m_read_trie_node_value) { - tsl_ht_assert(m_current_trie_node != nullptr); - - m_read_trie_node_value = false; - - anode_type* child = m_current_trie_node->first_child(); - if(child != nullptr) { - set_most_left_descendant_as_next_node(*child); - } - else if(m_current_trie_node->parent() != nullptr) { - trie_node_type* current_node_child = m_current_trie_node; - m_current_trie_node = m_current_trie_node->parent(); - - set_next_node_ascending(*current_node_child); - } - else { - set_as_end_iterator(); - } - } - else { - ++m_array_hash_iterator; - if(m_array_hash_iterator != m_array_hash_end_iterator) { - filter_prefix(); - } - // End of the road, set the iterator as an end node. - else if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - } - - - return *this; - } - - htrie_hash_iterator operator++(int) { - htrie_hash_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { - if(lhs.m_current_trie_node != rhs.m_current_trie_node || - lhs.m_read_trie_node_value != rhs.m_read_trie_node_value) - { - return false; - } - else if(lhs.m_read_trie_node_value) { - return true; - } - else { - if(lhs.m_current_hash_node != rhs.m_current_hash_node) { - return false; - } - else if(lhs.m_current_hash_node == nullptr) { - return true; - } - else { - return lhs.m_array_hash_iterator == rhs.m_array_hash_iterator && - lhs.m_array_hash_end_iterator == rhs.m_array_hash_end_iterator; - } - } - } - - friend bool operator!=(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { - return !(lhs == rhs); - } - - private: - void hash_node_prefix(std::basic_string& key_buffer_out) const { - tsl_ht_assert(!m_read_trie_node_value); - key_buffer_out.clear(); - - trie_node_type* tnode = m_current_trie_node; - while(tnode != nullptr && tnode->parent() != nullptr) { - key_buffer_out.push_back(tnode->child_of_char()); - tnode = tnode->parent(); - } - - std::reverse(key_buffer_out.begin(), key_buffer_out.end()); - - tsl_ht_assert(m_current_hash_node != nullptr); - if(m_current_hash_node->parent() != nullptr) { - key_buffer_out.push_back(m_current_hash_node->child_of_char()); - } - } - - template::type* = nullptr> - void filter_prefix() { - } - - template::type* = nullptr> - void filter_prefix() { - tsl_ht_assert(m_array_hash_iterator != m_array_hash_end_iterator); - tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); - - if(m_prefix_filter.empty()) { - return; - } - - while((m_prefix_filter.size() > m_array_hash_iterator.key_size() || - m_prefix_filter.compare(0, m_prefix_filter.size(), - m_array_hash_iterator.key(), m_prefix_filter.size()) != 0)) - { - ++m_array_hash_iterator; - if(m_array_hash_iterator == m_array_hash_end_iterator) { - if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - - return; - } - } - } - - /** - * Go back up in the tree to get the current_trie_node_child sibling. - * If none, try to go back up more in the tree to check the siblings of the ancestors. - */ - void set_next_node_ascending(anode_type& current_trie_node_child) { - tsl_ht_assert(m_current_trie_node != nullptr); - tsl_ht_assert(current_trie_node_child.parent() == m_current_trie_node); - - anode_type* next_node = m_current_trie_node->next_child(current_trie_node_child); - while(next_node == nullptr && m_current_trie_node->parent() != nullptr) { - anode_type* current_child = m_current_trie_node; - m_current_trie_node = m_current_trie_node->parent(); - next_node = m_current_trie_node->next_child(*current_child); - } - - // End of the road, set the iterator as an end node. - if(next_node == nullptr) { - set_as_end_iterator(); - } - else { - set_most_left_descendant_as_next_node(*next_node); - } - } - - void set_most_left_descendant_as_next_node(anode_type& search_start) { - if(search_start.is_hash_node()) { - set_current_hash_node(search_start.as_hash_node()); - } - else { - m_current_trie_node = &search_start.as_trie_node().most_left_descendant_value_trie_node(); - if(m_current_trie_node->val_node() != nullptr) { - m_read_trie_node_value = true; - } - else { - anode_type* first_child = m_current_trie_node->first_child(); - // a trie_node must either have a value_node or at least one child. - tsl_ht_assert(first_child != nullptr); - - set_current_hash_node(first_child->as_hash_node()); - } - } - } - - void set_current_hash_node(hash_node_type& hnode) { - tsl_ht_assert(!hnode.array_hash().empty()); - - m_current_hash_node = &hnode; - m_array_hash_iterator = m_current_hash_node->array_hash().begin(); - m_array_hash_end_iterator = m_current_hash_node->array_hash().end(); - } - - void set_as_end_iterator() { - m_current_trie_node = nullptr; - m_current_hash_node = nullptr; - m_read_trie_node_value = false; - } - - void skip_hash_node() { - tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); - if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - } - - private: - trie_node_type* m_current_trie_node; - hash_node_type* m_current_hash_node; - - array_hash_iterator_type m_array_hash_iterator; - array_hash_iterator_type m_array_hash_end_iterator; - - bool m_read_trie_node_value; - // TODO can't have void if !IsPrefixIterator, use inheritance - typename std::conditional, bool>::type m_prefix_filter; - }; - - - -public: - htrie_hash(const Hash& hash, float max_load_factor, size_type burst_threshold): - m_root(nullptr), m_nb_elements(0), - m_hash(hash), m_max_load_factor(max_load_factor) - { - this->burst_threshold(burst_threshold); - } - - htrie_hash(const htrie_hash& other): m_root(nullptr), m_nb_elements(other.m_nb_elements), - m_hash(other.m_hash), m_max_load_factor(other.m_max_load_factor), - m_burst_threshold(other.m_burst_threshold) - { - if(other.m_root != nullptr) { - if(other.m_root->is_hash_node()) { - m_root = make_unique(other.m_root->as_hash_node()); - } - else { - m_root = make_unique(other.m_root->as_trie_node()); - } - } - } - - htrie_hash(htrie_hash&& other) noexcept(std::is_nothrow_move_constructible::value) - : m_root(std::move(other.m_root)), - m_nb_elements(other.m_nb_elements), - m_hash(std::move(other.m_hash)), - m_max_load_factor(other.m_max_load_factor), - m_burst_threshold(other.m_burst_threshold) - { - other.clear(); - } - - htrie_hash& operator=(const htrie_hash& other) { - if(&other != this) { - std::unique_ptr new_root = nullptr; - if(other.m_root != nullptr) { - if(other.m_root->is_hash_node()) { - new_root = make_unique(other.m_root->as_hash_node()); - } - else { - new_root = make_unique(other.m_root->as_trie_node()); - } - } - - m_hash = other.m_hash; - m_root = std::move(new_root); - m_nb_elements = other.m_nb_elements; - m_max_load_factor = other.m_max_load_factor; - m_burst_threshold = other.m_burst_threshold; - } - - return *this; - } - - htrie_hash& operator=(htrie_hash&& other) { - other.swap(*this); - other.clear(); - - return *this; - } - - /* - * Iterators - */ - iterator begin() noexcept { - return mutable_iterator(cbegin()); - } - - const_iterator begin() const noexcept { - return cbegin(); - } - - const_iterator cbegin() const noexcept { - if(empty()) { - return cend(); - } - - return cbegin(*m_root); - } - - iterator end() noexcept { - iterator it; - it.set_as_end_iterator(); - - return it; - } - - const_iterator end() const noexcept { - return cend(); - } - - const_iterator cend() const noexcept { - const_iterator it; - it.set_as_end_iterator(); - - return it; - } - - - /* - * Capacity - */ - bool empty() const noexcept { - return m_nb_elements == 0; - } - - size_type size() const noexcept { - return m_nb_elements; - } - - size_type max_size() const noexcept { - return std::numeric_limits::max(); - } - - size_type max_key_size() const noexcept { - return array_hash_type::MAX_KEY_SIZE; - } - - void shrink_to_fit() { - auto first = begin(); - auto last = end(); - - while(first != last) { - if(first.m_read_trie_node_value) { - ++first; - } - else { - /* - * shrink_to_fit on array_hash will invalidate the iterators of array_hash. - * Save pointer to array_hash, skip the array_hash_node and then call - * shrink_to_fit on the saved pointer. - */ - hash_node* hnode = first.m_current_hash_node; - first.skip_hash_node(); - - tsl_ht_assert(hnode != nullptr); - hnode->array_hash().shrink_to_fit(); - } - } - } - - - /* - * Modifiers - */ - void clear() noexcept { - m_root.reset(nullptr); - m_nb_elements = 0; - } - - template - std::pair insert(const CharT* key, size_type key_size, ValueArgs&&... value_args) { - if(key_size > max_key_size()) { - THROW(std::length_error, "Key is too long."); - } - - if(m_root == nullptr) { - m_root = make_unique(m_hash, m_max_load_factor); - } - - return insert_impl(*m_root, key, key_size, std::forward(value_args)...); - } - - iterator erase(const_iterator pos) { - return erase(mutable_iterator(pos)); - } - - iterator erase(const_iterator first, const_iterator last) { - // TODO Optimize, could avoid the call to std::distance - const std::size_t nb_to_erase = std::size_t(std::distance(first, last)); - auto to_delete = mutable_iterator(first); - for(std::size_t i = 0; i < nb_to_erase; i++) { - to_delete = erase(to_delete); - } - - return to_delete; - } - - size_type erase(const CharT* key, size_type key_size) { - auto it = find(key, key_size); - if(it != end()) { - erase(it); - return 1; - } - else { - return 0; - } - - } - - size_type erase_prefix(const CharT* prefix, size_type prefix_size) { - if(m_root == nullptr) { - return 0; - } - - anode* current_node = m_root.get(); - for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->is_trie_node()) { - trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(prefix[iprefix]) == nullptr) { - return 0; - } - else { - current_node = tnode->child(prefix[iprefix]).get(); - } - } - else { - hash_node& hnode = current_node->as_hash_node(); - return erase_prefix_hash_node(hnode, prefix + iprefix, prefix_size - iprefix); - } - } - - - if(current_node->is_trie_node()) { - trie_node* parent = current_node->parent(); - - if(parent != nullptr) { - const size_type nb_erased = size_descendants(current_node->as_trie_node()); - - parent->set_child(current_node->child_of_char(), nullptr); - m_nb_elements -= nb_erased; - - if(parent->empty()) { - clear_empty_nodes(*parent); - } - - return nb_erased; - } - else { - const size_type nb_erased = m_nb_elements; - m_root.reset(nullptr); - m_nb_elements = 0; - - return nb_erased; - } - } - else { - const size_type nb_erased = current_node->as_hash_node().array_hash().size(); - - current_node->as_hash_node().array_hash().clear(); - m_nb_elements -= nb_erased; - - clear_empty_nodes(current_node->as_hash_node()); - - return nb_erased; - } - } - - void swap(htrie_hash& other) { - using std::swap; - - swap(m_hash, other.m_hash); - swap(m_root, other.m_root); - swap(m_nb_elements, other.m_nb_elements); - swap(m_max_load_factor, other.m_max_load_factor); - swap(m_burst_threshold, other.m_burst_threshold); - } - - /* - * Lookup - */ - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size) { - return const_cast(static_cast(this)->at(key, key_size)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size) const { - auto it_find = find(key, key_size); - if(it_find != cend()) { - return it_find.value(); - } - else { - THROW(std::out_of_range, "Couldn't find key."); - } - } - - //TODO optimize - template::value>::type* = nullptr> - U& access_operator(const CharT* key, size_type key_size) { - auto it_find = find(key, key_size); - if(it_find != cend()) { - return it_find.value(); - } - else { - return insert(key, key_size, U{}).first.value(); - } - } - - size_type count(const CharT* key, size_type key_size) const { - if(find(key, key_size) != cend()) { - return 1; - } - else { - return 0; - } - } - - iterator find(const CharT* key, size_type key_size) { - if(m_root == nullptr) { - return end(); - } - - return find_impl(*m_root, key, key_size); - } - - const_iterator find(const CharT* key, size_type key_size) const { - if(m_root == nullptr) { - return cend(); - } - - return find_impl(*m_root, key, key_size); - } - - std::pair equal_range(const CharT* key, size_type key_size) { - iterator it = find(key, key_size); - return std::make_pair(it, (it == end())?it:std::next(it)); - } - - std::pair equal_range(const CharT* key, size_type key_size) const { - const_iterator it = find(key, key_size); - return std::make_pair(it, (it == cend())?it:std::next(it)); - } - - std::pair equal_prefix_range(const CharT* prefix, size_type prefix_size) { - if(m_root == nullptr) { - return std::make_pair(prefix_end(), prefix_end()); - } - - return equal_prefix_range_impl(*m_root, prefix, prefix_size); - } - - std::pair equal_prefix_range(const CharT* prefix, - size_type prefix_size) const - { - if(m_root == nullptr) { - return std::make_pair(prefix_cend(), prefix_cend()); - } - - return equal_prefix_range_impl(*m_root, prefix, prefix_size); - } - - iterator longest_prefix(const CharT* key, size_type key_size) { - if(m_root == nullptr) { - return end(); - } - - return longest_prefix_impl(*m_root, key, key_size); - } - - const_iterator longest_prefix(const CharT* key, size_type key_size) const { - if(m_root == nullptr) { - return cend(); - } - - return longest_prefix_impl(*m_root, key, key_size); - } - - - /* - * Hash policy - */ - float max_load_factor() const { - return m_max_load_factor; - } - - void max_load_factor(float ml) { - m_max_load_factor = ml; - } - - /* - * Burst policy - */ - size_type burst_threshold() const { - return m_burst_threshold; - } - - void burst_threshold(size_type threshold) { - const size_type min_burst_threshold = MIN_BURST_THRESHOLD; - m_burst_threshold = std::max(min_burst_threshold, threshold); - } - - /* - * Observers - */ - hasher hash_function() const { - return m_hash; - } - - /* - * Other - */ - template - void serialize(Serializer& serializer) const { - serialize_impl(serializer); - } - - template - void deserialize(Deserializer& deserializer, bool hash_compatible) { - deserialize_impl(deserializer, hash_compatible); - } - -private: - /** - * Get the begin iterator by searching for the most left descendant node starting at search_start_node. - */ - template - Iterator cbegin(const anode& search_start_node) const noexcept { - if(search_start_node.is_hash_node()) { - return Iterator(search_start_node.as_hash_node()); - } - - const trie_node& tnode = search_start_node.as_trie_node().most_left_descendant_value_trie_node(); - if(tnode.val_node() != nullptr) { - return Iterator(tnode); - } - else { - const anode* first_child = tnode.first_child(); - tsl_ht_assert(first_child != nullptr); - - return Iterator(first_child->as_hash_node()); - } - } - - /** - * Get an iterator to the node that come just after the last descendant of search_start_node. - */ - template - Iterator cend(const anode& search_start_node) const noexcept { - if(search_start_node.parent() == nullptr) { - Iterator it; - it.set_as_end_iterator(); - - return it; - } - - const trie_node* current_trie_node = search_start_node.parent(); - const anode* next_node = current_trie_node->next_child(search_start_node); - - while(next_node == nullptr && current_trie_node->parent() != nullptr) { - const anode* current_child = current_trie_node; - current_trie_node = current_trie_node->parent(); - next_node = current_trie_node->next_child(*current_child); - } - - if(next_node == nullptr) { - Iterator it; - it.set_as_end_iterator(); - - return it; - } - else { - return cbegin(*next_node); - } - } - - prefix_iterator prefix_end() noexcept { - prefix_iterator it; - it.set_as_end_iterator(); - - return it; - } - - const_prefix_iterator prefix_cend() const noexcept { - const_prefix_iterator it; - it.set_as_end_iterator(); - - return it; - } - - size_type size_descendants(const anode& start_node) const { - auto first = cbegin(start_node); - auto last = cend(start_node); - - size_type nb_elements = 0; - while(first != last) { - if(first.m_read_trie_node_value) { - nb_elements++; - ++first; - } - else { - nb_elements += first.m_current_hash_node->array_hash().size(); - first.skip_hash_node(); - } - } - - return nb_elements; - } - - template - std::pair insert_impl(anode& search_start_node, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - anode* current_node = &search_start_node; - - for(size_type ikey = 0; ikey < key_size; ikey++) { - if(current_node->is_trie_node()) { - trie_node& tnode = current_node->as_trie_node(); - - if(tnode.child(key[ikey]) != nullptr) { - current_node = tnode.child(key[ikey]).get(); - } - else { - auto hnode = make_unique(m_hash, m_max_load_factor); - auto insert_it = hnode->array_hash().emplace_ks(key + ikey + 1, key_size - ikey - 1, - std::forward(value_args)...); - - tnode.set_child(key[ikey], std::move(hnode)); - m_nb_elements++; - - - return std::make_pair(iterator(tnode.child(key[ikey])->as_hash_node(), - insert_it.first), true); - } - } - else { - return insert_in_hash_node(current_node->as_hash_node(), - key + ikey, key_size - ikey, std::forward(value_args)...); - } - } - - - if(current_node->is_trie_node()) { - trie_node& tnode = current_node->as_trie_node(); - if(tnode.val_node() != nullptr) { - return std::make_pair(iterator(tnode), false); - } - else { - tnode.val_node() = make_unique(std::forward(value_args)...); - m_nb_elements++; - - return std::make_pair(iterator(tnode), true); - } - } - else { - return insert_in_hash_node(current_node->as_hash_node(), - "", 0, std::forward(value_args)...); - } - } - - template - std::pair insert_in_hash_node(hash_node& hnode, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - if(need_burst(hnode)) { - std::unique_ptr new_node = burst(hnode); - if(hnode.parent() == nullptr) { - tsl_ht_assert(m_root.get() == &hnode); - - m_root = std::move(new_node); - return insert_impl(*m_root, key, key_size, std::forward(value_args)...); - } - else { - trie_node* parent = hnode.parent(); - const CharT child_of_char = hnode.child_of_char(); - - parent->set_child(child_of_char, std::move(new_node)); - - return insert_impl(*parent->child(child_of_char), - key, key_size, std::forward(value_args)...); - } - } - else { - auto it_insert = hnode.array_hash().emplace_ks(key, key_size, - std::forward(value_args)...); - if(it_insert.second) { - m_nb_elements++; - } - - return std::make_pair(iterator(hnode, it_insert.first), it_insert.second); - } - } - - - iterator erase(iterator pos) { - iterator next_pos = std::next(pos); - - if(pos.m_read_trie_node_value) { - tsl_ht_assert(pos.m_current_trie_node != nullptr && pos.m_current_trie_node->val_node() != nullptr); - - pos.m_current_trie_node->val_node().reset(nullptr); - m_nb_elements--; - - if(pos.m_current_trie_node->empty()) { - clear_empty_nodes(*pos.m_current_trie_node); - } - - return next_pos; - } - else { - tsl_ht_assert(pos.m_current_hash_node != nullptr); - auto next_array_hash_it = pos.m_current_hash_node->array_hash().erase(pos.m_array_hash_iterator); - m_nb_elements--; - - if(next_array_hash_it != pos.m_current_hash_node->array_hash().end()) { - // The erase on array_hash invalidated the next_pos iterator, return the right one. - return iterator(*pos.m_current_hash_node, next_array_hash_it); - } - else { - if(pos.m_current_hash_node->array_hash().empty()) { - clear_empty_nodes(*pos.m_current_hash_node); - } - - return next_pos; - } - } - } - - /** - * Clear all the empty nodes from the tree starting from empty_node (empty for a hash_node means that - * the array hash is empty, for a trie_node it means the node doesn't have any child or value_node - * associated to it). - */ - void clear_empty_nodes(anode& empty_node) noexcept { - tsl_ht_assert(!empty_node.is_trie_node() || - (empty_node.as_trie_node().empty() && empty_node.as_trie_node().val_node() == nullptr)); - tsl_ht_assert(!empty_node.is_hash_node() || empty_node.as_hash_node().array_hash().empty()); - - - trie_node* parent = empty_node.parent(); - if(parent == nullptr) { - tsl_ht_assert(m_root.get() == &empty_node); - tsl_ht_assert(m_nb_elements == 0); - m_root.reset(nullptr); - } - else if(parent->val_node() != nullptr || parent->nb_children() > 1) { - parent->child(empty_node.child_of_char()).reset(nullptr); - } - else if(parent->parent() == nullptr) { - tsl_ht_assert(m_root.get() == empty_node.parent()); - tsl_ht_assert(m_nb_elements == 0); - m_root.reset(nullptr); - } - else { - /** - * Parent is empty if we remove its empty_node child. - * Put empty_node as new child of the grand parent instead of parent (move hnode up, - * and delete the parent). And recurse. - * - * We can't just set grand_parent->child(parent->child_of_char()) to nullptr as - * the grand_parent may also become empty. We don't want empty trie_node with no value_node - * in the tree. - */ - trie_node* grand_parent = parent->parent(); - grand_parent->set_child(parent->child_of_char(), - std::move(parent->child(empty_node.child_of_char()))); - - - clear_empty_nodes(empty_node); - } - } - - - - - iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) { - return mutable_iterator(static_cast(this)->find_impl(search_start_node, key, key_size)); - } - - const_iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) const { - const anode* current_node = &search_start_node; - - for(size_type ikey = 0; ikey < key_size; ikey++) { - if(current_node->is_trie_node()) { - const trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(key[ikey]) == nullptr) { - return cend(); - } - else { - current_node = tnode->child(key[ikey]).get(); - } - } - else { - return find_in_hash_node(current_node->as_hash_node(), - key + ikey, key_size - ikey); - } - } - - - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - return (tnode.val_node() != nullptr)?const_iterator(tnode):cend(); - } - else { - return find_in_hash_node(current_node->as_hash_node(), "", 0); - } - } - - const_iterator find_in_hash_node(const hash_node& hnode, - const CharT* key, size_type key_size) const - { - auto it = hnode.array_hash().find_ks(key, key_size); - if(it != hnode.array_hash().end()) { - return const_iterator(hnode, it); - } - else { - return cend(); - } - } - - - iterator longest_prefix_impl(const anode& search_start_node, - const CharT* value, size_type value_size) - { - return mutable_iterator(static_cast(this)->longest_prefix_impl(search_start_node, - value, value_size)); - } - - const_iterator longest_prefix_impl(const anode& search_start_node, - const CharT* value, size_type value_size) const - { - const anode* current_node = &search_start_node; - const_iterator longest_found_prefix = cend(); - - for(size_type ivalue = 0; ivalue < value_size; ivalue++) { - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - - if(tnode.val_node() != nullptr) { - longest_found_prefix = const_iterator(tnode); - } - - if(tnode.child(value[ivalue]) == nullptr) { - return longest_found_prefix; - } - else { - current_node = tnode.child(value[ivalue]).get(); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - - /** - * Test the presence in the hash node of each substring from the - * remaining [ivalue, value_size) string starting from the longest. - * Also test the empty string. - */ - for(std::size_t i = ivalue; i <= value_size; i++) { - auto it = hnode.array_hash().find_ks(value + ivalue, (value_size - i)); - if(it != hnode.array_hash().end()) { - return const_iterator(hnode, it); - } - } - - return longest_found_prefix; - } - } - - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - - if(tnode.val_node() != nullptr) { - longest_found_prefix = const_iterator(tnode); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - - auto it = hnode.array_hash().find_ks("", 0); - if(it != hnode.array_hash().end()) { - longest_found_prefix = const_iterator(hnode, it); - } - } - - return longest_found_prefix; - } - - - std::pair equal_prefix_range_impl( - anode& search_start_node, - const CharT* prefix, size_type prefix_size) - { - auto range = static_cast(this)->equal_prefix_range_impl(search_start_node, - prefix, prefix_size); - return std::make_pair(mutable_iterator(range.first), mutable_iterator(range.second)); - } - - std::pair equal_prefix_range_impl( - const anode& search_start_node, - const CharT* prefix, size_type prefix_size) const - { - const anode* current_node = &search_start_node; - - for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->is_trie_node()) { - const trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(prefix[iprefix]) == nullptr) { - return std::make_pair(prefix_cend(), prefix_cend()); - } - else { - current_node = tnode->child(prefix[iprefix]).get(); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - const_prefix_iterator begin(hnode.parent(), &hnode, - hnode.array_hash().begin(), hnode.array_hash().end(), - false, std::basic_string(prefix + iprefix, prefix_size - iprefix)); - begin.filter_prefix(); - - const_prefix_iterator end = cend(*current_node); - - return std::make_pair(begin, end); - } - } - - - const_prefix_iterator begin = cbegin(*current_node); - const_prefix_iterator end = cend(*current_node); - - return std::make_pair(begin, end); - } - - size_type erase_prefix_hash_node(hash_node& hnode, const CharT* prefix, size_type prefix_size) { - size_type nb_erased = 0; - - auto it = hnode.array_hash().begin(); - while(it != hnode.array_hash().end()) { - if(it.key_size() >= prefix_size && - std::memcmp(prefix, it.key(), prefix_size * sizeof(CharT)) == 0) - { - it = hnode.array_hash().erase(it); - ++nb_erased; - --m_nb_elements; - } - else { - ++it; - } - } - - return nb_erased; - } - - - /* - * Burst - */ - bool need_burst(hash_node& node) const { - return node.array_hash().size() >= m_burst_threshold; - } - - - /** - * Burst the node and use the copy constructor instead of move constructor for the values. - * Also use this method for trivial value types like int, int*, ... as it requires - * less book-keeping (thus faster) than the burst using move constructors. - */ - template::value && - std::is_copy_constructible::value && - (!std::is_nothrow_move_constructible::value || - !std::is_nothrow_move_assignable::value || - std::is_arithmetic::value || - std::is_pointer::value)>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - const std::array first_char_count = - get_first_char_count(node.array_hash().cbegin(), - node.array_hash().cend()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(it.value()); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, it.value()); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - /** - * Burst the node and use the move constructor and move assign operator - */ - template::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value && - !std::is_arithmetic::value && - !std::is_pointer::value>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - /** - * We burst the node->array_hash() into multiple arrays hash. While doing so, we move each value in - * the node->array_hash() into the new arrays hash. After each move, we save a pointer to where the value - * has been moved. In case of exception, we rollback these values into the original node->array_hash(). - */ - std::vector moved_values_rollback; - moved_values_rollback.reserve(node.array_hash().size()); - - const std::array first_char_count = - get_first_char_count(node.array_hash().cbegin(), node.array_hash().cend()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().begin(); it != node.array_hash().end(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(std::move(it.value())); - moved_values_rollback.push_back(std::addressof(new_node->val_node()->m_value)); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - auto it_insert = hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, - std::move(it.value())); - moved_values_rollback.push_back(std::addressof(it_insert.first.value())); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - template::value>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - const std::array first_char_count = - get_first_char_count(node.array_hash().begin(), node.array_hash().end()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - std::array get_first_char_count(typename array_hash_type::const_iterator begin, - typename array_hash_type::const_iterator end) const - { - std::array count{{}}; - for(auto it = begin; it != end; ++it) { - if(it.key_size() == 0) { - continue; - } - - count[as_position(it.key()[0])]++; - } - - return count; - } - - - hash_node& get_hash_node_for_char(const std::array& first_char_count, - trie_node& tnode, CharT for_char) - { - if(tnode.child(for_char) == nullptr) { - const size_type nb_buckets = - size_type( - std::ceil(float(first_char_count[as_position(for_char)] + - HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT/2) - / m_max_load_factor - )); - - tnode.set_child(for_char, - make_unique(nb_buckets, m_hash, m_max_load_factor)); - } - - return tnode.child(for_char)->as_hash_node(); - } - - iterator mutable_iterator(const_iterator it) noexcept { - // end iterator or reading from a trie node value - if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { - typename array_hash_type::iterator default_it; - - return iterator(const_cast(it.m_current_trie_node), nullptr, - default_it, default_it, it.m_read_trie_node_value); - } - else { - hash_node* hnode = const_cast(it.m_current_hash_node); - return iterator(const_cast(it.m_current_trie_node), hnode, - hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), - hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), - it.m_read_trie_node_value); - } - } - - prefix_iterator mutable_iterator(const_prefix_iterator it) noexcept { - // end iterator or reading from a trie node value - if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { - typename array_hash_type::iterator default_it; - - return prefix_iterator(const_cast(it.m_current_trie_node), nullptr, - default_it, default_it, it.m_read_trie_node_value, ""); - } - else { - hash_node* hnode = const_cast(it.m_current_hash_node); - return prefix_iterator(const_cast(it.m_current_trie_node), hnode, - hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), - hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), - it.m_read_trie_node_value, it.m_prefix_filter); - } - } - - template - void serialize_impl(Serializer& serializer) const { - const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; - serializer(version); - - const slz_size_type nb_elements = m_nb_elements; - serializer(nb_elements); - - const float max_load_factor = m_max_load_factor; - serializer(max_load_factor); - - const slz_size_type burst_threshold = m_burst_threshold; - serializer(burst_threshold); - - - std::basic_string str_buffer; - - auto it = begin(); - auto last = end(); - - while(it != last) { - // Serialize trie node value - if(it.m_read_trie_node_value) { - const CharT node_type = static_cast::type>(slz_node_type::TRIE_NODE); - serializer(&node_type, 1); - - it.key(str_buffer); - - const slz_size_type str_size = str_buffer.size(); - serializer(str_size); - serializer(str_buffer.data(), str_buffer.size()); - serialize_value(serializer, it); - - - ++it; - } - // Serialize hash node values - else { - const CharT node_type = static_cast::type>(slz_node_type::HASH_NODE); - serializer(&node_type, 1); - - it.hash_node_prefix(str_buffer); - - const slz_size_type str_size = str_buffer.size(); - serializer(str_size); - serializer(str_buffer.data(), str_buffer.size()); - - const hash_node* hnode = it.m_current_hash_node; - tsl_ht_assert(hnode != nullptr); - hnode->array_hash().serialize(serializer); - - - it.skip_hash_node(); - } - } - } - - template::value>::type* = nullptr> - void serialize_value(Serializer& /*serializer*/, const_iterator /*it*/) const { - } - - template::value>::type* = nullptr> - void serialize_value(Serializer& serializer, const_iterator it) const { - serializer(it.value()); - } - - template - void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { - tsl_ht_assert(m_nb_elements == 0 && m_root == nullptr); // Current trie must be empty - - const slz_size_type version = deserialize_value(deserializer); - // For now we only have one version of the serialization protocol. - // If it doesn't match there is a problem with the file. - if(version != SERIALIZATION_PROTOCOL_VERSION) { - THROW(std::runtime_error, "Can't deserialize the htrie_map/set. The protocol version header is invalid."); - } - - - const slz_size_type nb_elements = deserialize_value(deserializer); - const float max_load_factor = deserialize_value(deserializer); - const slz_size_type burst_threshold = deserialize_value(deserializer); - - this->burst_threshold(numeric_cast(burst_threshold, "Deserialized burst_threshold is too big.")); - this->max_load_factor(max_load_factor); - - - std::vector str_buffer; - while(m_nb_elements < nb_elements) { - CharT node_type_marker; - deserializer(&node_type_marker, 1); - - static_assert(std::is_same::type>::value, ""); - const slz_node_type node_type = static_cast(node_type_marker); - if(node_type == slz_node_type::TRIE_NODE) { - const std::size_t str_size = numeric_cast(deserialize_value(deserializer), - "Deserialized str_size is too big."); - - str_buffer.resize(str_size); - deserializer(str_buffer.data(), str_size); - - - trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size); - deserialize_value_node(deserializer, current_node); - m_nb_elements++; - } - else if(node_type == slz_node_type::HASH_NODE) { - const std::size_t str_size = numeric_cast(deserialize_value(deserializer), - "Deserialized str_size is too big."); - - if(str_size == 0) { - tsl_ht_assert(m_nb_elements == 0 && !m_root); - - m_root = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); - m_nb_elements += m_root->as_hash_node().array_hash().size(); - - tsl_ht_assert(m_nb_elements == nb_elements); - } - else { - str_buffer.resize(str_size); - deserializer(str_buffer.data(), str_size); - - - auto hnode = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); - m_nb_elements += hnode->array_hash().size(); - - trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size - 1); - current_node->set_child(str_buffer[str_size - 1], std::move(hnode)); - } - } - else { - THROW(std::runtime_error, "Unknown deserialized node type."); - } - } - - tsl_ht_assert(m_nb_elements == nb_elements); - } - - trie_node* insert_prefix_trie_nodes(const CharT* prefix, std::size_t prefix_size) { - if(m_root == nullptr) { - m_root = make_unique(); - } - - trie_node* current_node = &m_root->as_trie_node(); - for(std::size_t iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->child(prefix[iprefix]) == nullptr) { - current_node->set_child(prefix[iprefix], make_unique()); - } - - current_node = ¤t_node->child(prefix[iprefix])->as_trie_node(); - } - - return current_node; - } - - template::value>::type* = nullptr> - void deserialize_value_node(Deserializer& /*deserializer*/, trie_node* current_node) { - tsl_ht_assert(!current_node->val_node()); - current_node->val_node() = make_unique(); - } - - template::value>::type* = nullptr> - void deserialize_value_node(Deserializer& deserializer, trie_node* current_node) { - tsl_ht_assert(!current_node->val_node()); - current_node->val_node() = make_unique(deserialize_value(deserializer)); - } - - template - static U deserialize_value(Deserializer& deserializer) { - // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword - #if defined (_MSC_VER) && _MSC_VER < 1910 - return deserializer.Deserializer::operator()(); - #else - return deserializer.Deserializer::template operator()(); - #endif - } - - // Same as std::make_unique for non-array types which is only available in C++14 (we need to support C++11). - template - static std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new U(std::forward(args)...)); - } - -public: - static constexpr float HASH_NODE_DEFAULT_MAX_LOAD_FACTOR = 8.0f; - static const size_type DEFAULT_BURST_THRESHOLD = 16384; - -private: - - /** - * Fixed size type used to represent size_type values on serialization. Need to be big enough - * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. - */ - using slz_size_type = std::uint64_t; - enum class slz_node_type: CharT { TRIE_NODE = 0, HASH_NODE = 1 }; - - /** - * Protocol version currenlty used for serialization. - */ - static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; - - static const size_type HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT = 32; - static const size_type MIN_BURST_THRESHOLD = 4; - - std::unique_ptr m_root; - size_type m_nb_elements; - Hash m_hash; - float m_max_load_factor; - size_type m_burst_threshold; - -}; - -} // end namespace detail_htrie_hash -} // end namespace tsl - -#endif diff --git a/ios/include/trie/htrie_map.h b/ios/include/trie/htrie_map.h deleted file mode 100644 index 59712c5e..00000000 --- a/ios/include/trie/htrie_map.h +++ /dev/null @@ -1,647 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_MAP_H -#define TSL_HTRIE_MAP_H - -#include -#include -#include -#include -#include -#include "htrie_hash.h" - -namespace tsl { - -/** - * Implementation of a hat-trie map. - * - * The value T must be either nothrow move-constructible/assignable, copy-constructible or both. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. - * See max_key_size() for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - */ -template, - class KeySizeT = std::uint16_t> -class htrie_map { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_htrie_hash::htrie_hash; - -public: - using char_type = typename ht::char_type; - using mapped_type = T; - using key_size_type = typename ht::key_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - using prefix_iterator = typename ht::prefix_iterator; - using const_prefix_iterator = typename ht::const_prefix_iterator; - -public: - explicit htrie_map(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - ht::DEFAULT_BURST_THRESHOLD) - { - } - - explicit htrie_map(size_type burst_threshold, - const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - burst_threshold) - { - } - - template::value>::type* = nullptr> - htrie_map(InputIt first, InputIt last, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(first, last); - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_map(std::initializer_list, T>> init, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(init); - } -#else - htrie_map(std::initializer_list> init, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_map& operator=(std::initializer_list, T>> ilist) { - clear(); - insert(ilist); - - return *this; - } -#else - htrie_map& operator=(std::initializer_list> ilist) { - clear(); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - - /** - * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. - */ - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - - std::pair insert_ks(const CharT* key, size_type key_size, const T& value) { - return m_ht.insert(key, key_size, value); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, const T& value) { - return m_ht.insert(key.data(), key.size(), value); - } -#else - std::pair insert(const CharT* key, const T& value) { - return m_ht.insert(key, std::strlen(key), value); - } - - std::pair insert(const std::basic_string& key, const T& value) { - return m_ht.insert(key.data(), key.size(), value); - } -#endif - - - - std::pair insert_ks(const CharT* key, size_type key_size, T&& value) { - return m_ht.insert(key, key_size, std::move(value)); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, T&& value) { - return m_ht.insert(key.data(), key.size(), std::move(value)); - } -#else - std::pair insert(const CharT* key, T&& value) { - return m_ht.insert(key, std::strlen(key), std::move(value)); - } - - std::pair insert(const std::basic_string& key, T&& value) { - return m_ht.insert(key.data(), key.size(), std::move(value)); - } -#endif - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - for(auto it = first; it != last; ++it) { - insert_pair(*it); - } - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - void insert(std::initializer_list, T>> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - - template - std::pair emplace_ks(const CharT* key, size_type key_size, Args&&... args) { - return m_ht.insert(key, key_size, std::forward(args)...); - } -#ifdef TSL_HT_HAS_STRING_VIEW - template - std::pair emplace(const std::basic_string_view& key, Args&&... args) { - return m_ht.insert(key.data(), key.size(), std::forward(args)...); - } -#else - template - std::pair emplace(const CharT* key, Args&&... args) { - return m_ht.insert(key, std::strlen(key), std::forward(args)...); - } - - template - std::pair emplace(const std::basic_string& key, Args&&... args) { - return m_ht.insert(key.data(), key.size(), std::forward(args)...); - } -#endif - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::strlen(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - - - - /** - * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. - */ - size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.erase_prefix(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string_view& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const CharT* prefix) { - return m_ht.erase_prefix(prefix, std::strlen(prefix)); - } - - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#endif - - - - void swap(htrie_map& other) { other.m_ht.swap(m_ht); } - - /* - * Lookup - */ - T& at_ks(const CharT* key, size_type key_size) { return m_ht.at(key, key_size); } - const T& at_ks(const CharT* key, size_type key_size) const { return m_ht.at(key, key_size); } - -#ifdef TSL_HT_HAS_STRING_VIEW - T& at(const std::basic_string_view& key) { return m_ht.at(key.data(), key.size()); } - const T& at(const std::basic_string_view& key) const { return m_ht.at(key.data(), key.size()); } -#else - T& at(const CharT* key) { return m_ht.at(key, std::strlen(key)); } - const T& at(const CharT* key) const { return m_ht.at(key, std::strlen(key)); } - - T& at(const std::basic_string& key) { return m_ht.at(key.data(), key.size()); } - const T& at(const std::basic_string& key) const { return m_ht.at(key.data(), key.size()); } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - T& operator[](const std::basic_string_view& key) { return m_ht.access_operator(key.data(), key.size()); } -#else - T& operator[](const CharT* key) { return m_ht.access_operator(key, std::strlen(key)); } - T& operator[](const std::basic_string& key) { return m_ht.access_operator(key.data(), key.size()); } -#endif - - - - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - - - - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::strlen(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::strlen(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - - - - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - - - /** - * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair - * of iterator, the first being the begin iterator and the second being the end iterator. - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.equal_prefix_range(prefix, prefix_size); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { - return m_ht.equal_prefix_range(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) const { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#endif - - - - /** - * Return the element in the trie which is the longest prefix of `key`. If no - * element in the trie is a prefix of `key`, the end iterator is returned. - * - * Example: - * - * tsl::htrie_map map = {{"/foo", 1}, {"/foo/bar", 1}}; - * - * map.longest_prefix("/foo"); // returns {"/foo", 1} - * map.longest_prefix("/foo/baz"); // returns {"/foo", 1} - * map.longest_prefix("/foo/bar/baz"); // returns {"/foo/bar", 1} - * map.longest_prefix("/foo/bar/"); // returns {"/foo/bar", 1} - * map.longest_prefix("/bar"); // returns end() - * map.longest_prefix(""); // returns end() - */ - iterator longest_prefix_ks(const CharT* key, size_type key_size) { - return m_ht.longest_prefix(key, key_size); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { - return m_ht.longest_prefix(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string_view& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string_view& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#else - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const CharT* key) { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const CharT* key) const { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#endif - - - - /* - * Hash policy - */ - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - - /* - * Burst policy - */ - size_type burst_threshold() const { return m_ht.burst_threshold(); } - void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - - - - /* - * Other - */ - - /** - * Serialize the map through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - - /** - * Deserialize a previously serialized map through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash map part of the hat-trie is hash compatible with the serialized map, the deserialization process - * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * and KeySizeT must behave the same than the ones used in the serialized map. Otherwise the behaviour is undefined - * with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` and `T` of the `htrie_map` are not the same as the - * types used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static htrie_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { - htrie_map map; - map.m_ht.deserialize(deserializer, hash_compatible); - - return map; - } - - friend bool operator==(const htrie_map& lhs, const htrie_map& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - std::string key_buffer; - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - it.key(key_buffer); - - const auto it_element_rhs = rhs.find(key_buffer); - if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const htrie_map& lhs, const htrie_map& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(htrie_map& lhs, htrie_map& rhs) { - lhs.swap(rhs); - } - -private: - template - void insert_pair(const std::pair& value) { - insert(value.first, value.second); - } - - template - void insert_pair(std::pair&& value) { - insert(value.first, std::move(value.second)); - } - -private: - ht m_ht; -}; - -} // end namespace tsl - -#endif diff --git a/ios/include/trie/htrie_set.h b/ios/include/trie/htrie_set.h deleted file mode 100644 index e2f40adc..00000000 --- a/ios/include/trie/htrie_set.h +++ /dev/null @@ -1,586 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_SET_H -#define TSL_HTRIE_SET_H - -#include -#include -#include -#include -#include -#include "htrie_hash.h" - -namespace tsl { - -/** - * Implementation of a hat-trie set. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. - * See max_key_size() for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert: always invalidate the iterators. - * - erase: always invalidate the iterators. - */ -template, - class KeySizeT = std::uint16_t> -class htrie_set { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_htrie_hash::htrie_hash; - -public: - using char_type = typename ht::char_type; - using key_size_type = typename ht::key_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - using prefix_iterator = typename ht::prefix_iterator; - using const_prefix_iterator = typename ht::const_prefix_iterator; - -public: - explicit htrie_set(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - ht::DEFAULT_BURST_THRESHOLD) - { - } - - explicit htrie_set(size_type burst_threshold, - const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - burst_threshold) - { - } - - template::value>::type* = nullptr> - htrie_set(InputIt first, InputIt last, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(first, last); - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_set(std::initializer_list> init, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(init); - } -#else - htrie_set(std::initializer_list init, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_set& operator=(std::initializer_list> ilist) { - clear(); - insert(ilist); - - return *this; - } -#else - htrie_set& operator=(std::initializer_list ilist) { - clear(); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - - /** - * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. - */ - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - - std::pair insert_ks(const CharT* key, size_type key_size) { - return m_ht.insert(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key) { - return m_ht.insert(key.data(), key.size()); - } -#else - std::pair insert(const CharT* key) { - return m_ht.insert(key, std::strlen(key)); - } - - std::pair insert(const std::basic_string& key) { - return m_ht.insert(key.data(), key.size()); - } -#endif - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - for(auto it = first; it != last; ++it) { - insert(*it); - } - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - - std::pair emplace_ks(const CharT* key, size_type key_size) { - return m_ht.insert(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair emplace(const std::basic_string_view& key) { - return m_ht.insert(key.data(), key.size()); - } -#else - std::pair emplace(const CharT* key) { - return m_ht.insert(key, std::strlen(key)); - } - - std::pair emplace(const std::basic_string& key) { - return m_ht.insert(key.data(), key.size()); - } -#endif - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::strlen(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - - - - /** - * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. - */ - size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.erase_prefix(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string_view& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const CharT* prefix) { - return m_ht.erase_prefix(prefix, std::strlen(prefix)); - } - - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#endif - - - - void swap(htrie_set& other) { other.m_ht.swap(m_ht); } - - - /* - * Lookup - */ - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - - - - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::strlen(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::strlen(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - - - - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - - - - /** - * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair - * of iterator, the first being the begin iterator and the second being the end iterator. - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.equal_prefix_range(prefix, prefix_size); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { - return m_ht.equal_prefix_range(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) const { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#endif - - - - /** - * Return the element in the trie which is the longest prefix of `key`. If no - * element in the trie is a prefix of `key`, the end iterator is returned. - * - * Example: - * - * tsl::htrie_set set = {"/foo", "/foo/bar"}; - * - * set.longest_prefix("/foo"); // returns "/foo" - * set.longest_prefix("/foo/baz"); // returns "/foo" - * set.longest_prefix("/foo/bar/baz"); // returns "/foo/bar" - * set.longest_prefix("/foo/bar/"); // returns "/foo/bar" - * set.longest_prefix("/bar"); // returns end() - * set.longest_prefix(""); // returns end() - */ - iterator longest_prefix_ks(const CharT* key, size_type key_size) { - return m_ht.longest_prefix(key, key_size); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { - return m_ht.longest_prefix(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string_view& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string_view& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#else - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const CharT* key) { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const CharT* key) const { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#endif - - - - /* - * Hash policy - */ - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - - /* - * Burst policy - */ - size_type burst_threshold() const { return m_ht.burst_threshold(); } - void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - - - - /* - * Other - */ - - /** - * Serialize the set through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - - /** - * Deserialize a previously serialized set through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash set part of the hat-trie is hash compatible with the serialized set, the deserialization process - * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * and KeySizeT must behave the same than the ones used in the serialized set. Otherwise the behaviour is undefined - * with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` of the `htrie_set` is not the same as the - * type used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static htrie_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { - htrie_set set; - set.m_ht.deserialize(deserializer, hash_compatible); - - return set; - } - - friend bool operator==(const htrie_set& lhs, const htrie_set& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - std::string key_buffer; - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - it.key(key_buffer); - - const auto it_element_rhs = rhs.find(key_buffer); - if(it_element_rhs == rhs.cend()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const htrie_set& lhs, const htrie_set& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(htrie_set& lhs, htrie_set& rhs) { - lhs.swap(rhs); - } - -private: - ht m_ht; -}; - -} // end namespace tsl - -#endif diff --git a/ios/include/tsl/array-hash/array_growth_policy.h b/ios/include/tsl/array-hash/array_growth_policy.h deleted file mode 100644 index 641e0cb7..00000000 --- a/ios/include/tsl/array-hash/array_growth_policy.h +++ /dev/null @@ -1,307 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_GROWTH_POLICY_H -#define TSL_ARRAY_GROWTH_POLICY_H - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef __EXCEPTIONS -# define THROW(_e, _m) throw _e(_m) -#else -# include -# ifndef NDEBUG -# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0) -# else -# define THROW(_e, _m) std::terminate() -# endif -#endif - - -namespace tsl { -namespace ah { - -/** - * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows - * the table to use a mask operation instead of a modulo operation to map a hash to a bucket. - * - * GrowthFactor must be a power of two >= 2. - */ -template -class power_of_two_growth_policy { -public: - /** - * Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter. - * This number is a minimum, the policy may update this value with a higher value if needed (but not lower). - * - * If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and - * bucket_for_hash must always return 0 in this case. - */ - explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) { - if(min_bucket_count_in_out > max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(min_bucket_count_in_out > 0) { - min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out); - m_mask = min_bucket_count_in_out - 1; - } - else { - m_mask = 0; - } - } - - /** - * Return the bucket [0, bucket_count()) to which the hash belongs. - * If bucket_count() is 0, it must always return 0. - */ - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return hash & m_mask; - } - - /** - * Return the number of buckets that should be used on next growth. - */ - std::size_t next_bucket_count() const { - if((m_mask + 1) > max_bucket_count() / GrowthFactor) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - return (m_mask + 1) * GrowthFactor; - } - - /** - * Return the maximum number of buckets supported by the policy. - */ - std::size_t max_bucket_count() const { - // Largest power of two. - return (std::numeric_limits::max() / 2) + 1; - } - - /** - * Reset the growth policy as if it was created with a bucket count of 0. - * After a clear, the policy must always return 0 when bucket_for_hash is called. - */ - void clear() noexcept { - m_mask = 0; - } - -private: - static std::size_t round_up_to_power_of_two(std::size_t value) { - if(is_power_of_two(value)) { - return value; - } - - if(value == 0) { - return 1; - } - - --value; - for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { - value |= value >> i; - } - - return value + 1; - } - - static constexpr bool is_power_of_two(std::size_t value) { - return value != 0 && (value & (value - 1)) == 0; - } - -protected: - static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2."); - - std::size_t m_mask; -}; - - -/** - * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash - * to a bucket. Slower but it can be useful if you want a slower growth. - */ -template> -class mod_growth_policy { -public: - explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) { - if(min_bucket_count_in_out > max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(min_bucket_count_in_out > 0) { - m_mod = min_bucket_count_in_out; - } - else { - m_mod = 1; - } - } - - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return hash % m_mod; - } - - std::size_t next_bucket_count() const { - if(m_mod == max_bucket_count()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR); - if(!std::isnormal(next_bucket_count)) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - if(next_bucket_count > double(max_bucket_count())) { - return max_bucket_count(); - } - else { - return std::size_t(next_bucket_count); - } - } - - std::size_t max_bucket_count() const { - return MAX_BUCKET_COUNT; - } - - void clear() noexcept { - m_mod = 1; - } - -private: - static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den; - static const std::size_t MAX_BUCKET_COUNT = - std::size_t(double( - std::numeric_limits::max() / REHASH_SIZE_MULTIPLICATION_FACTOR - )); - - static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1."); - - std::size_t m_mod; -}; - - - -namespace detail { - -static constexpr const std::array PRIMES = {{ - 1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, - 1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, - 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, - 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul -}}; - -template -static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; } - -// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the -// compiler can optimize the modulo code better with a constant known at the compilation. -static constexpr const std::array MOD_PRIME = {{ - &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, - &mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>, - &mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, - &mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39> -}}; - -} - -/** - * Grow the hash table by using prime numbers as bucket count. Slower than tsl::ah::power_of_two_growth_policy in - * general but will probably distribute the values around better in the buckets with a poor hash function. - * - * To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers. - * - * With a switch the code would look like: - * \code - * switch(iprime) { // iprime is the current prime of the hash table - * case 0: hash % 5ul; - * break; - * case 1: hash % 17ul; - * break; - * case 2: hash % 29ul; - * break; - * ... - * } - * \endcode - * - * Due to the constant variable in the modulo the compiler is able to optimize the operation - * by a series of multiplications, substractions and shifts. - * - * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment. - */ -class prime_growth_policy { -public: - explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) { - auto it_prime = std::lower_bound(detail::PRIMES.begin(), - detail::PRIMES.end(), min_bucket_count_in_out); - if(it_prime == detail::PRIMES.end()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - m_iprime = static_cast(std::distance(detail::PRIMES.begin(), it_prime)); - if(min_bucket_count_in_out > 0) { - min_bucket_count_in_out = *it_prime; - } - else { - min_bucket_count_in_out = 0; - } - } - - std::size_t bucket_for_hash(std::size_t hash) const noexcept { - return detail::MOD_PRIME[m_iprime](hash); - } - - std::size_t next_bucket_count() const { - if(m_iprime + 1 >= detail::PRIMES.size()) { - THROW(std::length_error, "The hash table exceeds its maximum size."); - } - - return detail::PRIMES[m_iprime + 1]; - } - - std::size_t max_bucket_count() const { - return detail::PRIMES.back(); - } - - void clear() noexcept { - m_iprime = 0; - } - -private: - unsigned int m_iprime; - - static_assert(std::numeric_limits::max() >= detail::PRIMES.size(), - "The type of m_iprime is not big enough."); -}; - -} -} - -#endif diff --git a/ios/include/tsl/array-hash/array_hash.h b/ios/include/tsl/array-hash/array_hash.h deleted file mode 100644 index ccb204ca..00000000 --- a/ios/include/tsl/array-hash/array_hash.h +++ /dev/null @@ -1,1766 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_HASH_H -#define TSL_ARRAY_HASH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array_growth_policy.h" - - -/* - * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433), - * check also __cplusplus version. - */ -#ifdef __has_include -# if __has_include() && __cplusplus >= 201703L -# define TSL_AH_HAS_STRING_VIEW -# endif -#endif - - -#ifdef TSL_AH_HAS_STRING_VIEW -# include -#endif - - -#ifdef TSL_DEBUG -# define tsl_ah_assert(expr) assert(expr) -#else -# define tsl_ah_assert(expr) (static_cast(0)) -#endif - - - -/** - * Implementation of the array hash structure described in the - * "Cache-conscious collision resolution in string hash tables." (Askitis Nikolas and Justin Zobel, 2005) paper. - */ -namespace tsl { - -namespace ah { - -template -struct str_hash { -#ifdef TSL_AH_HAS_STRING_VIEW - std::size_t operator()(const CharT* key, std::size_t key_size) const { - return std::hash>()(std::basic_string_view(key, key_size)); - } -#else - /** - * FNV-1a hash - */ - std::size_t operator()(const CharT* key, std::size_t key_size) const { - static const std::size_t init = std::size_t((sizeof(std::size_t) == 8)?0xcbf29ce484222325:0x811c9dc5); - static const std::size_t multiplier = std::size_t((sizeof(std::size_t) == 8)?0x100000001b3:0x1000193); - - std::size_t hash = init; - for (std::size_t i = 0; i < key_size; ++i) { - hash ^= key[i]; - hash *= multiplier; - } - - return hash; - } -#endif -}; - -template -struct str_equal { - bool operator()(const CharT* key_lhs, std::size_t key_size_lhs, - const CharT* key_rhs, std::size_t key_size_rhs) const - { - if(key_size_lhs != key_size_rhs) { - return false; - } - else { - return std::memcmp(key_lhs, key_rhs, key_size_lhs * sizeof(CharT)) == 0; - } - } -}; -} - - -namespace detail_array_hash { - -template -struct is_iterator: std::false_type { -}; - -template -struct is_iterator::iterator_category, void>::value>::type>: std::true_type { -}; - -static constexpr bool is_power_of_two(std::size_t value) { - return value != 0 && (value & (value - 1)) == 0; -} - -template -static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { - T ret = static_cast(value); - if(static_cast(ret) != value) { - THROW(std::runtime_error, error_message); - } - - const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || - (std::is_signed::value && std::is_signed::value); - if(!is_same_signedness && (ret < T{}) != (value < U{})) { - THROW(std::runtime_error, error_message); - } - - return ret; -} - - - -/** - * Fixed size type used to represent size_type values on serialization. Need to be big enough - * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. - */ -using slz_size_type = std::uint64_t; - -template -static T deserialize_value(Deserializer& deserializer) { - // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword -#if defined (_MSC_VER) && _MSC_VER < 1910 - return deserializer.Deserializer::operator()(); -#else - return deserializer.Deserializer::template operator()(); -#endif -} - -/** - * For each string in the bucket, store the size of the string, the chars of the string - * and T, if it's not void. T should be either void or an unsigned type. - * - * End the buffer with END_OF_BUCKET flag. END_OF_BUCKET has the same type as the string size variable. - * - * m_buffer (CharT*): - * | size of str1 (KeySizeT) | str1 (const CharT*) | value (T if T != void) | ... | - * | size of strN (KeySizeT) | strN (const CharT*) | value (T if T != void) | END_OF_BUCKET (KeySizeT) | - * - * m_buffer is null if there is no string in the bucket. - * - * KeySizeT and T are extended to be a multiple of CharT when stored in the buffer. - * - * Use std::malloc and std::free instead of new and delete so we can have access to std::realloc. - */ -template -class array_bucket { - template - using has_mapped_type = typename std::integral_constant::value>; - - static_assert(!has_mapped_type::value || std::is_unsigned::value, - "T should be either void or an unsigned type."); - - static_assert(std::is_unsigned::value, "KeySizeT should be an unsigned type."); - -public: - template - class array_bucket_iterator; - - using char_type = CharT; - using key_size_type = KeySizeT; - using mapped_type = T; - using size_type = std::size_t; - using key_equal = KeyEqual; - using iterator = array_bucket_iterator; - using const_iterator = array_bucket_iterator; - - static_assert(sizeof(KeySizeT) <= sizeof(size_type), "sizeof(KeySizeT) should be <= sizeof(std::size_t;)"); - static_assert(std::is_unsigned::value, ""); - -private: - /** - * Return how much space in bytes the type U will take when stored in the buffer. - * As the buffer is of type CharT, U may take more space than sizeof(U). - * - * Example: sizeof(CharT) = 4, sizeof(U) = 2 => U will take 4 bytes in the buffer instead of 2. - */ - template - static constexpr size_type sizeof_in_buff() noexcept { - static_assert(is_power_of_two(sizeof(U)), "sizeof(U) should be a power of two."); - static_assert(is_power_of_two(sizeof(CharT)), "sizeof(CharT) should be a power of two."); - - return std::max(sizeof(U), sizeof(CharT)); - } - - /** - * Same as sizeof_in_buff, but instead of returning the size in bytes return it in term of sizeof(CharT). - */ - template - static constexpr size_type size_as_char_t() noexcept { - return sizeof_in_buff() / sizeof(CharT); - } - - static key_size_type read_key_size(const CharT* buffer) noexcept { - key_size_type key_size; - std::memcpy(&key_size, buffer, sizeof(key_size)); - - return key_size; - } - - static mapped_type read_value(const CharT* buffer) noexcept { - mapped_type value; - std::memcpy(&value, buffer, sizeof(value)); - - return value; - } - - static bool is_end_of_bucket(const CharT* buffer) noexcept { - return read_key_size(buffer) == END_OF_BUCKET; - } - -public: - /** - * Return the size required for an entry with a key of size 'key_size'. - */ - template::value>::type* = nullptr> - static size_type entry_required_bytes(size_type key_size) noexcept { - return sizeof_in_buff() + (key_size + KEY_EXTRA_SIZE) * sizeof(CharT); - } - - template::value>::type* = nullptr> - static size_type entry_required_bytes(size_type key_size) noexcept { - return sizeof_in_buff() + (key_size + KEY_EXTRA_SIZE) * sizeof(CharT) + - sizeof_in_buff(); - } - -private: - /** - * Return the size of the current entry in buffer. - */ - static size_type entry_size_bytes(const CharT* buffer) noexcept { - return entry_required_bytes(read_key_size(buffer)); - } - -public: - template - class array_bucket_iterator { - friend class array_bucket; - - using buffer_type = typename std::conditional::type; - - explicit array_bucket_iterator(buffer_type* position) noexcept: m_position(position) { - } - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = void; - using difference_type = std::ptrdiff_t; - using reference = void; - using pointer = void; - - public: - array_bucket_iterator() noexcept: m_position(nullptr) { - } - - const CharT* key() const { - return m_position + size_as_char_t(); - } - - size_type key_size() const { - return read_key_size(m_position); - } - - template::value>::type* = nullptr> - U value() const { - return read_value(m_position + size_as_char_t() + key_size() + KEY_EXTRA_SIZE); - } - - - template::value && !IsConst && std::is_same::value>::type* = nullptr> - void set_value(U value) noexcept { - std::memcpy(m_position + size_as_char_t() + key_size() + KEY_EXTRA_SIZE, - &value, sizeof(value)); - } - - array_bucket_iterator& operator++() { - m_position += entry_size_bytes(m_position)/sizeof(CharT); - if(is_end_of_bucket(m_position)) { - m_position = nullptr; - } - - return *this; - } - - array_bucket_iterator operator++(int) { - array_bucket_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const array_bucket_iterator& lhs, const array_bucket_iterator& rhs) { - return lhs.m_position == rhs.m_position; - } - - friend bool operator!=(const array_bucket_iterator& lhs, const array_bucket_iterator& rhs) { - return !(lhs == rhs); - } - - private: - buffer_type* m_position; - }; - - - - static iterator end_it() noexcept { - return iterator(nullptr); - } - - static const_iterator cend_it() noexcept { - return const_iterator(nullptr); - } - -public: - array_bucket(): m_buffer(nullptr) { - } - - /** - * Reserve 'size' in the buffer of the bucket. The created bucket is empty. - */ - array_bucket(std::size_t size): m_buffer(nullptr) { - if(size == 0) { - return; - } - - m_buffer = static_cast(std::malloc(size*sizeof(CharT) + sizeof_in_buff())); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(m_buffer, &end_of_bucket, sizeof(end_of_bucket)); - } - - ~array_bucket() { - clear(); - } - - array_bucket(const array_bucket& other) { - if(other.m_buffer == nullptr) { - m_buffer = nullptr; - return; - } - - const size_type other_buffer_size = other.size(); - m_buffer = static_cast(std::malloc(other_buffer_size*sizeof(CharT) + sizeof_in_buff())); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - std::memcpy(m_buffer, other.m_buffer, other_buffer_size*sizeof(CharT)); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(m_buffer + other_buffer_size, &end_of_bucket, sizeof(end_of_bucket)); - } - - array_bucket(array_bucket&& other) noexcept: m_buffer(other.m_buffer) { - other.m_buffer = nullptr; - } - - array_bucket& operator=(array_bucket other) noexcept { - other.swap(*this); - - return *this; - } - - void swap(array_bucket& other) noexcept { - std::swap(m_buffer, other.m_buffer); - } - - iterator begin() noexcept { return iterator(m_buffer); } - iterator end() noexcept { return iterator(nullptr); } - const_iterator begin() const noexcept { return cbegin(); } - const_iterator end() const noexcept { return cend(); } - const_iterator cbegin() const noexcept { return const_iterator(m_buffer); } - const_iterator cend() const noexcept { return const_iterator(nullptr); } - - /** - * Return an iterator pointing to the key entry if presents or, if not there, to the position - * past the last element of the bucket. Return end() if the bucket has not be initialized yet. - * - * The boolean of the pair is set to true if the key is there, false otherwise. - */ - std::pair find_or_end_of_bucket(const CharT* key, size_type key_size) const noexcept { - if(m_buffer == nullptr) { - return std::make_pair(cend(), false); - } - - const CharT* buffer_ptr_in_out = m_buffer; - const bool found = find_or_end_of_bucket_impl(key, key_size, buffer_ptr_in_out); - - return std::make_pair(const_iterator(buffer_ptr_in_out), found); - } - - /** - * Append the element 'key' with its potential value at the end of the bucket. - * 'end_of_bucket' should point past the end of the last element in the bucket, end() if the bucket - * was not initialized yet. You usually get this value from find_or_end_of_bucket. - * - * Return the position where the element was actually inserted. - */ - template - const_iterator append(const_iterator end_of_bucket, const CharT* key, size_type key_size, - ValueArgs&&... value) - { - const key_size_type key_sz = as_key_size_type(key_size); - - if(end_of_bucket == cend()) { - tsl_ah_assert(m_buffer == nullptr); - - const size_type buffer_size = entry_required_bytes(key_sz) + sizeof_in_buff(); - - m_buffer = static_cast(std::malloc(buffer_size)); - if(m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - append_impl(key, key_sz, m_buffer, std::forward(value)...); - - return const_iterator(m_buffer); - } - else { - tsl_ah_assert(is_end_of_bucket(end_of_bucket.m_position)); - - const size_type current_size = ((end_of_bucket.m_position + size_as_char_t()) - - m_buffer) * sizeof(CharT); - const size_type new_size = current_size + entry_required_bytes(key_sz); - - - CharT* new_buffer = static_cast(std::realloc(m_buffer, new_size)); - if(new_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - m_buffer = new_buffer; - - - CharT* buffer_append_pos = m_buffer + current_size / sizeof(CharT) - - size_as_char_t(); - append_impl(key, key_sz, buffer_append_pos, std::forward(value)...); - - return const_iterator(buffer_append_pos); - } - - } - - const_iterator erase(const_iterator position) noexcept { - tsl_ah_assert(position.m_position != nullptr && !is_end_of_bucket(position.m_position)); - - // get mutable pointers - CharT* start_entry = m_buffer + (position.m_position - m_buffer); - CharT* start_next_entry = start_entry + entry_size_bytes(start_entry) / sizeof(CharT); - - - CharT* end_buffer_ptr = start_next_entry; - while(!is_end_of_bucket(end_buffer_ptr)) { - end_buffer_ptr += entry_size_bytes(end_buffer_ptr) / sizeof(CharT); - } - end_buffer_ptr += size_as_char_t(); - - - const size_type size_to_move = (end_buffer_ptr - start_next_entry) * sizeof(CharT); - std::memmove(start_entry, start_next_entry, size_to_move); - - - if(is_end_of_bucket(m_buffer)) { - clear(); - return cend(); - } - else if(is_end_of_bucket(start_entry)) { - return cend(); - } - else { - return const_iterator(start_entry); - } - } - - /** - * Return true if an element has been erased - */ - bool erase(const CharT* key, size_type key_size) noexcept { - if(m_buffer == nullptr) { - return false; - } - - const CharT* entry_buffer_ptr_in_out = m_buffer; - bool found = find_or_end_of_bucket_impl(key, key_size, entry_buffer_ptr_in_out); - if(found) { - erase(const_iterator(entry_buffer_ptr_in_out)); - - return true; - } - else { - return false; - } - } - - /** - * Bucket should be big enough and there is no check to see if the key already exists. - * No check on key_size. - */ - template - void append_in_reserved_bucket_no_check(const CharT* key, size_type key_size, ValueArgs&&... value) noexcept { - CharT* buffer_ptr = m_buffer; - while(!is_end_of_bucket(buffer_ptr)) { - buffer_ptr += entry_size_bytes(buffer_ptr)/sizeof(CharT); - } - - append_impl(key, key_size_type(key_size), buffer_ptr, std::forward(value)...); - } - - bool empty() const noexcept { - return m_buffer == nullptr || is_end_of_bucket(m_buffer); - } - - void clear() noexcept { - std::free(m_buffer); - m_buffer = nullptr; - } - - iterator mutable_iterator(const_iterator pos) noexcept { - return iterator(m_buffer + (pos.m_position - m_buffer)); - } - - template - void serialize(Serializer& serializer) const { - const slz_size_type bucket_size = size(); - tsl_ah_assert(m_buffer != nullptr || bucket_size == 0); - - serializer(bucket_size); - serializer(m_buffer, bucket_size); - } - - template - static array_bucket deserialize(Deserializer& deserializer) { - array_bucket bucket; - const slz_size_type bucket_size_ds = deserialize_value(deserializer); - - if(bucket_size_ds == 0) { - return bucket; - } - - const std::size_t bucket_size = numeric_cast(bucket_size_ds, "Deserialized bucket_size is too big."); - bucket.m_buffer = static_cast(std::malloc(bucket_size*sizeof(CharT) + sizeof_in_buff())); - if(bucket.m_buffer == nullptr) { - THROW(std::runtime_error, "Out of memory"); - } - - - deserializer(bucket.m_buffer, bucket_size); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(bucket.m_buffer + bucket_size, &end_of_bucket, sizeof(end_of_bucket)); - - - tsl_ah_assert(bucket.size() == bucket_size); - return bucket; - } - -private: - key_size_type as_key_size_type(size_type key_size) const { - if(key_size > MAX_KEY_SIZE) { - THROW(std::length_error, "Key is too long."); - } - - return key_size_type(key_size); - } - - /* - * Return true if found, false otherwise. - * If true, buffer_ptr_in_out points to the start of the entry matching 'key'. - * If false, buffer_ptr_in_out points to where the 'key' should be inserted. - * - * Start search from buffer_ptr_in_out. - */ - bool find_or_end_of_bucket_impl(const CharT* key, size_type key_size, - const CharT* & buffer_ptr_in_out) const noexcept - { - while(!is_end_of_bucket(buffer_ptr_in_out)) { - const key_size_type buffer_key_size = read_key_size(buffer_ptr_in_out); - const CharT* buffer_str = buffer_ptr_in_out + size_as_char_t(); - if(KeyEqual()(buffer_str, buffer_key_size, key, key_size)) { - return true; - } - - buffer_ptr_in_out += entry_size_bytes(buffer_ptr_in_out)/sizeof(CharT); - } - - return false; - } - - template::value>::type* = nullptr> - void append_impl(const CharT* key, key_size_type key_size, CharT* buffer_append_pos) noexcept { - std::memcpy(buffer_append_pos, &key_size, sizeof(key_size)); - buffer_append_pos += size_as_char_t(); - - std::memcpy(buffer_append_pos, key, key_size * sizeof(CharT)); - buffer_append_pos += key_size; - - const CharT zero = 0; - std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE * sizeof(CharT)); - buffer_append_pos += KEY_EXTRA_SIZE; - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(buffer_append_pos, &end_of_bucket, sizeof(end_of_bucket)); - } - - template::value>::type* = nullptr> - void append_impl(const CharT* key, key_size_type key_size, CharT* buffer_append_pos, - typename array_bucket::mapped_type value) noexcept - { - std::memcpy(buffer_append_pos, &key_size, sizeof(key_size)); - buffer_append_pos += size_as_char_t(); - - std::memcpy(buffer_append_pos, key, key_size * sizeof(CharT)); - buffer_append_pos += key_size; - - const CharT zero = 0; - std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE * sizeof(CharT)); - buffer_append_pos += KEY_EXTRA_SIZE; - - std::memcpy(buffer_append_pos, &value, sizeof(value)); - buffer_append_pos += size_as_char_t(); - - const auto end_of_bucket = END_OF_BUCKET; - std::memcpy(buffer_append_pos, &end_of_bucket, sizeof(end_of_bucket)); - } - - /** - * Return the number of CharT in m_buffer. As the size of the buffer is not stored to gain some space, - * the method need to find the EOF marker and is thus in O(n). - */ - size_type size() const noexcept { - if(m_buffer == nullptr) { - return 0; - } - - CharT* buffer_ptr = m_buffer; - while(!is_end_of_bucket(buffer_ptr)) { - buffer_ptr += entry_size_bytes(buffer_ptr)/sizeof(CharT); - } - - return buffer_ptr - m_buffer; - } - -private: - static const key_size_type END_OF_BUCKET = std::numeric_limits::max(); - static const key_size_type KEY_EXTRA_SIZE = StoreNullTerminator?1:0; - - CharT* m_buffer; - -public: - static const key_size_type MAX_KEY_SIZE = - // -1 for END_OF_BUCKET - key_size_type(std::numeric_limits::max() - KEY_EXTRA_SIZE - 1); -}; - - -template -class value_container { -public: - void clear() noexcept { - m_values.clear(); - } - - void reserve(std::size_t new_cap) { - m_values.reserve(new_cap); - } - - void shrink_to_fit() { - m_values.shrink_to_fit(); - } - - friend void swap(value_container& lhs, value_container& rhs) { - lhs.m_values.swap(rhs.m_values); - } - -protected: - static constexpr float VECTOR_GROWTH_RATE = 1.5f; - - // TODO use a sparse array? or a std::deque - std::vector m_values; -}; - -template<> -class value_container { -public: - void clear() noexcept { - } - - void shrink_to_fit() { - } - - void reserve(std::size_t /*new_cap*/) { - } -}; - - - -/** - * If there is no value in the array_hash (in the case of a set for example), T should be void. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * - * The number of elements in the map is limited to std::numeric_limits::max(). - */ -template -class array_hash: private value_container, private Hash, private GrowthPolicy { -private: - template - using has_mapped_type = typename std::integral_constant::value>; - - /** - * If there is a mapped type in array_hash, we store the values in m_values of value_container class - * and we store an index to m_values in the bucket. The index is of type IndexSizeT. - */ - using array_bucket = tsl::detail_array_hash::array_bucket::value, - IndexSizeT, - void>::type, - KeyEqual, KeySizeT, StoreNullTerminator>; - -public: - template - class array_hash_iterator; - - using char_type = CharT; - using key_size_type = KeySizeT; - using index_size_type = IndexSizeT; - using size_type = std::size_t; - using hasher = Hash; - using key_equal = KeyEqual; - using iterator = array_hash_iterator; - using const_iterator = array_hash_iterator; - - -/* - * Iterator classes - */ -public: - template - class array_hash_iterator { - friend class array_hash; - - private: - using iterator_array_bucket = typename array_bucket::const_iterator; - - using iterator_buckets = typename std::conditional::const_iterator, - typename std::vector::iterator>::type; - - using array_hash_ptr = typename std::conditional::type; - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = typename std::conditional::value, T, void>::type; - using difference_type = std::ptrdiff_t; - using reference = typename std::conditional::value, - typename std::conditional< - IsConst, - typename std::add_lvalue_reference::type, - typename std::add_lvalue_reference::type>::type, - void>::type; - using pointer = typename std::conditional::value, - typename std::conditional::type, - void>::type; - - - private: - array_hash_iterator(iterator_buckets buckets_iterator, iterator_array_bucket array_bucket_iterator, - array_hash_ptr array_hash_p) noexcept: - m_buckets_iterator(buckets_iterator), - m_array_bucket_iterator(array_bucket_iterator), - m_array_hash(array_hash_p) - { - tsl_ah_assert(m_array_hash != nullptr); - } - - public: - array_hash_iterator() noexcept: m_array_hash(nullptr) { - } - - template::type* = nullptr> - array_hash_iterator(const array_hash_iterator& other) noexcept : - m_buckets_iterator(other.m_buckets_iterator), - m_array_bucket_iterator(other.m_array_bucket_iterator), - m_array_hash(other.m_array_hash) - { - } - - array_hash_iterator(const array_hash_iterator& other) = default; - array_hash_iterator(array_hash_iterator&& other) = default; - array_hash_iterator& operator=(const array_hash_iterator& other) = default; - array_hash_iterator& operator=(array_hash_iterator&& other) = default; - - const CharT* key() const { - return m_array_bucket_iterator.key(); - } - - size_type key_size() const { - return m_array_bucket_iterator.key_size(); - } - -#ifdef TSL_AH_HAS_STRING_VIEW - std::basic_string_view key_sv() const { - return std::basic_string_view(key(), key_size()); - } -#endif - - template::value>::type* = nullptr> - reference value() const { - return this->m_array_hash->m_values[value_position()]; - } - - template::value>::type* = nullptr> - reference operator*() const { - return value(); - } - - template::value>::type* = nullptr> - pointer operator->() const { - return std::addressof(value()); - } - - array_hash_iterator& operator++() { - tsl_ah_assert(m_buckets_iterator != m_array_hash->m_buckets_data.end()); - tsl_ah_assert(m_array_bucket_iterator != m_buckets_iterator->cend()); - - ++m_array_bucket_iterator; - if(m_array_bucket_iterator == m_buckets_iterator->cend()) { - do { - ++m_buckets_iterator; - } while(m_buckets_iterator != m_array_hash->m_buckets_data.end() && - m_buckets_iterator->empty()); - - if(m_buckets_iterator != m_array_hash->m_buckets_data.end()) { - m_array_bucket_iterator = m_buckets_iterator->cbegin(); - } - } - - return *this; - } - - array_hash_iterator operator++(int) { - array_hash_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const array_hash_iterator& lhs, const array_hash_iterator& rhs) { - return lhs.m_buckets_iterator == rhs.m_buckets_iterator && - lhs.m_array_bucket_iterator == rhs.m_array_bucket_iterator && - lhs.m_array_hash == rhs.m_array_hash; - } - - friend bool operator!=(const array_hash_iterator& lhs, const array_hash_iterator& rhs) { - return !(lhs == rhs); - } - - private: - template::value>::type* = nullptr> - IndexSizeT value_position() const { - return this->m_array_bucket_iterator.value(); - } - - private: - iterator_buckets m_buckets_iterator; - iterator_array_bucket m_array_bucket_iterator; - - array_hash_ptr m_array_hash; - }; - - - -public: - array_hash(size_type bucket_count, - const Hash& hash, - float max_load_factor): value_container(), - Hash(hash), - GrowthPolicy(bucket_count), - m_buckets_data(bucket_count > max_bucket_count()? - max_bucket_count(): - bucket_count), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(0) - { - this->max_load_factor(max_load_factor); - } - - array_hash(const array_hash& other): value_container(other), - Hash(other), - GrowthPolicy(other), - m_buckets_data(other.m_buckets_data), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(other.m_nb_elements), - m_max_load_factor(other.m_max_load_factor), - m_load_threshold(other.m_load_threshold) - { - } - - array_hash(array_hash&& other) noexcept(std::is_nothrow_move_constructible>::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_constructible>::value) - : value_container(std::move(other)), - Hash(std::move(other)), - GrowthPolicy(std::move(other)), - m_buckets_data(std::move(other.m_buckets_data)), - m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()), - m_nb_elements(other.m_nb_elements), - m_max_load_factor(other.m_max_load_factor), - m_load_threshold(other.m_load_threshold) - { - other.value_container::clear(); - other.GrowthPolicy::clear(); - other.m_buckets_data.clear(); - other.m_buckets = static_empty_bucket_ptr(); - other.m_nb_elements = 0; - other.m_load_threshold = 0; - } - - array_hash& operator=(const array_hash& other) { - if(&other != this) { - value_container::operator=(other); - Hash::operator=(other); - GrowthPolicy::operator=(other); - - m_buckets_data = other.m_buckets_data; - m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr(): - m_buckets_data.data(); - m_nb_elements = other.m_nb_elements; - m_max_load_factor = other.m_max_load_factor; - m_load_threshold = other.m_load_threshold; - } - - return *this; - } - - array_hash& operator=(array_hash&& other) { - other.swap(*this); - other.clear(); - - return *this; - } - - - /* - * Iterators - */ - iterator begin() noexcept { - auto begin = m_buckets_data.begin(); - while(begin != m_buckets_data.end() && begin->empty()) { - ++begin; - } - - return (begin != m_buckets_data.end())?iterator(begin, begin->cbegin(), this):end(); - } - - const_iterator begin() const noexcept { - return cbegin(); - } - - const_iterator cbegin() const noexcept { - auto begin = m_buckets_data.cbegin(); - while(begin != m_buckets_data.cend() && begin->empty()) { - ++begin; - } - - return (begin != m_buckets_data.cend())?const_iterator(begin, begin->cbegin(), this):cend(); - } - - iterator end() noexcept { - return iterator(m_buckets_data.end(), array_bucket::cend_it(), this); - } - - const_iterator end() const noexcept { - return cend(); - } - - const_iterator cend() const noexcept { - return const_iterator(m_buckets_data.end(), array_bucket::cend_it(), this); - } - - - /* - * Capacity - */ - bool empty() const noexcept { - return m_nb_elements == 0; - } - - size_type size() const noexcept { - return m_nb_elements; - } - - size_type max_size() const noexcept { - return std::numeric_limits::max(); - } - - size_type max_key_size() const noexcept { - return MAX_KEY_SIZE; - } - - void shrink_to_fit() { - clear_old_erased_values(); - value_container::shrink_to_fit(); - - rehash_impl(size_type(std::ceil(float(size())/max_load_factor()))); - } - - /* - * Modifiers - */ - void clear() noexcept { - value_container::clear(); - - for(auto& bucket: m_buckets_data) { - bucket.clear(); - } - - m_nb_elements = 0; - } - - - - template - std::pair emplace(const CharT* key, size_type key_size, ValueArgs&&... value_args) { - const std::size_t hash = hash_key(key, key_size); - std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it_find.first, this), false); - } - - if(grow_on_high_load()) { - ibucket = bucket_for_hash(hash); - it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - } - - return emplace_impl(ibucket, it_find.first, key, key_size, std::forward(value_args)...); - } - - template - std::pair insert_or_assign(const CharT* key, size_type key_size, M&& obj) { - auto it = emplace(key, key_size, std::forward(obj)); - if(!it.second) { - it.first.value() = std::forward(obj); - } - - return it; - } - - - - iterator erase(const_iterator pos) { - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - return erase_from_bucket(mutable_iterator(pos)); - } - - iterator erase(const_iterator first, const_iterator last) { - if(first == last) { - return mutable_iterator(first); - } - - /** - * When erasing an element from a bucket with erase_from_bucket, it invalidates all the iterators - * in the array bucket of the element (m_array_bucket_iterator) but not the iterators of the buckets - * itself (m_buckets_iterator). - * - * So first erase all the values between first and last which are not part of the bucket of last, - * and then erase carefully the values in last's bucket. - */ - auto to_delete = mutable_iterator(first); - while(to_delete.m_buckets_iterator != last.m_buckets_iterator) { - to_delete = erase_from_bucket(to_delete); - } - - std::size_t nb_elements_until_last = std::distance(to_delete.m_array_bucket_iterator, - last.m_array_bucket_iterator); - while(nb_elements_until_last > 0) { - to_delete = erase_from_bucket(to_delete); - nb_elements_until_last--; - } - - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - return to_delete; - } - - - - size_type erase(const CharT* key, size_type key_size) { - return erase(key, key_size, hash_key(key, key_size)); - } - - size_type erase(const CharT* key, size_type key_size, std::size_t hash) { - if(should_clear_old_erased_values()) { - clear_old_erased_values(); - } - - const std::size_t ibucket = bucket_for_hash(hash); - if(m_buckets[ibucket].erase(key, key_size)) { - m_nb_elements--; - return 1; - } - else { - return 0; - } - } - - - - void swap(array_hash& other) { - using std::swap; - - swap(static_cast&>(*this), static_cast&>(other)); - swap(static_cast(*this), static_cast(other)); - swap(static_cast(*this), static_cast(other)); - swap(m_buckets_data, other.m_buckets_data); - swap(m_buckets, other.m_buckets); - swap(m_nb_elements, other.m_nb_elements); - swap(m_max_load_factor, other.m_max_load_factor); - swap(m_load_threshold, other.m_load_threshold); - } - - /* - * Lookup - */ - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size) { - return at(key, key_size, hash_key(key, key_size)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size) const { - return at(key, key_size, hash_key(key, key_size)); - } - - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size, std::size_t hash) { - return const_cast(static_cast(this)->at(key, key_size, hash)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return this->m_values[it_find.first.value()]; - } - else { - THROW(std::out_of_range, "Couldn't find key."); - } - } - - - - template::value>::type* = nullptr> - U& access_operator(const CharT* key, size_type key_size) { - const std::size_t hash = hash_key(key, key_size); - std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return this->m_values[it_find.first.value()]; - } - else { - if(grow_on_high_load()) { - ibucket = bucket_for_hash(hash); - it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - } - - return emplace_impl(ibucket, it_find.first, key, key_size, U{}).first.value(); - } - } - - - - size_type count(const CharT* key, size_type key_size) const { - return count(key, key_size, hash_key(key, key_size)); - } - - size_type count(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return 1; - } - else { - return 0; - } - } - - - - iterator find(const CharT* key, size_type key_size) { - return find(key, key_size, hash_key(key, key_size)); - } - - const_iterator find(const CharT* key, size_type key_size) const { - return find(key, key_size, hash_key(key, key_size)); - } - - iterator find(const CharT* key, size_type key_size, std::size_t hash) { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return iterator(m_buckets_data.begin() + ibucket, it_find.first, this); - } - else { - return end(); - } - } - - const_iterator find(const CharT* key, size_type key_size, std::size_t hash) const { - const std::size_t ibucket = bucket_for_hash(hash); - - auto it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size); - if(it_find.second) { - return const_iterator(m_buckets_data.cbegin() + ibucket, it_find.first, this); - } - else { - return cend(); - } - } - - - - std::pair equal_range(const CharT* key, size_type key_size) { - return equal_range(key, key_size, hash_key(key, key_size)); - } - - std::pair equal_range(const CharT* key, size_type key_size) const { - return equal_range(key, key_size, hash_key(key, key_size)); - } - - std::pair equal_range(const CharT* key, size_type key_size, std::size_t hash) { - iterator it = find(key, key_size, hash); - return std::make_pair(it, (it == end())?it:std::next(it)); - } - - std::pair equal_range(const CharT* key, size_type key_size, - std::size_t hash) const - { - const_iterator it = find(key, key_size, hash); - return std::make_pair(it, (it == cend())?it:std::next(it)); - } - - /* - * Bucket interface - */ - size_type bucket_count() const { - return m_buckets_data.size(); - } - - size_type max_bucket_count() const { - return std::min(GrowthPolicy::max_bucket_count(), m_buckets_data.max_size()); - } - - - /* - * Hash policy - */ - float load_factor() const { - if(bucket_count() == 0) { - return 0; - } - - return float(m_nb_elements) / float(bucket_count()); - } - - float max_load_factor() const { - return m_max_load_factor; - } - - void max_load_factor(float ml) { - m_max_load_factor = std::max(0.1f, ml); - m_load_threshold = size_type(float(bucket_count())*m_max_load_factor); - } - - void rehash(size_type count) { - count = std::max(count, size_type(std::ceil(float(size())/max_load_factor()))); - rehash_impl(count); - } - - void reserve(size_type count) { - rehash(size_type(std::ceil(float(count)/max_load_factor()))); - } - - /* - * Observers - */ - hasher hash_function() const { - return static_cast(*this); - } - - // TODO add support for statefull KeyEqual - key_equal key_eq() const { - return KeyEqual(); - } - - /* - * Other - */ - iterator mutable_iterator(const_iterator it) noexcept { - auto it_bucket = m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), it.m_buckets_iterator); - return iterator(it_bucket, it.m_array_bucket_iterator, this); - } - - template - void serialize(Serializer& serializer) const { - serialize_impl(serializer); - } - - template - void deserialize(Deserializer& deserializer, bool hash_compatible) { - deserialize_impl(deserializer, hash_compatible); - } - -private: - std::size_t hash_key(const CharT* key, size_type key_size) const { - return Hash::operator()(key, key_size); - } - - std::size_t bucket_for_hash(std::size_t hash) const { - return GrowthPolicy::bucket_for_hash(hash); - } - - /** - * If there is a mapped_type, the mapped value in m_values is not erased now. - * It will be erased when the ratio between the size of the map and - * the size of the map + the number of deleted values still stored is low enough (see clear_old_erased_values). - */ - iterator erase_from_bucket(iterator pos) noexcept { - auto array_bucket_next_it = pos.m_buckets_iterator->erase(pos.m_array_bucket_iterator); - m_nb_elements--; - - if(array_bucket_next_it != pos.m_buckets_iterator->cend()) { - return iterator(pos.m_buckets_iterator, array_bucket_next_it, this); - } - else { - do { - ++pos.m_buckets_iterator; - } while(pos.m_buckets_iterator != m_buckets_data.end() && pos.m_buckets_iterator->empty()); - - if(pos.m_buckets_iterator != m_buckets_data.end()) { - return iterator(pos.m_buckets_iterator, pos.m_buckets_iterator->cbegin(), this); - } - else { - return end(); - } - } - } - - - template::value>::type* = nullptr> - bool should_clear_old_erased_values(float /*threshold*/ = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD) const { - return false; - } - - template::value>::type* = nullptr> - bool should_clear_old_erased_values(float threshold = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD) const { - if(this->m_values.size() == 0) { - return false; - } - - return float(m_nb_elements)/float(this->m_values.size()) < threshold; - } - - template::value>::type* = nullptr> - void clear_old_erased_values() { - } - - template::value>::type* = nullptr> - void clear_old_erased_values() { - static_assert(std::is_nothrow_move_constructible::value || - std::is_copy_constructible::value, - "mapped_value must be either copy constructible or nothrow move constructible."); - - if(m_nb_elements == this->m_values.size()) { - return; - } - - std::vector new_values; - new_values.reserve(size()); - - for(auto it = begin(); it != end(); ++it) { - new_values.push_back(std::move_if_noexcept(it.value())); - } - - - IndexSizeT ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - auto it_array_bucket = it.m_buckets_iterator->mutable_iterator(it.m_array_bucket_iterator); - it_array_bucket.set_value(ivalue); - ivalue++; - } - - new_values.swap(this->m_values); - tsl_ah_assert(m_nb_elements == this->m_values.size()); - } - - /** - * Return true if a rehash occurred. - */ - bool grow_on_high_load() { - if(size() >= m_load_threshold) { - rehash_impl(GrowthPolicy::next_bucket_count()); - return true; - } - - return false; - } - - template::value>::type* = nullptr> - std::pair emplace_impl(std::size_t ibucket, typename array_bucket::const_iterator end_of_bucket, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - if(this->m_values.size() >= max_size()) { - // Try to clear old erased values lingering in m_values. Throw if it doesn't change anything. - clear_old_erased_values(); - if(this->m_values.size() >= max_size()) { - THROW(std::length_error, "Can't insert value, too much values in the map."); - } - } - - if(this->m_values.size() == this->m_values.capacity()) { - this->m_values.reserve(std::size_t(float(this->m_values.size()) * value_container::VECTOR_GROWTH_RATE)); - } - - - this->m_values.emplace_back(std::forward(value_args)...); - - auto it = m_buckets[ibucket].append(end_of_bucket, key, key_size, IndexSizeT(this->m_values.size() - 1)); - m_nb_elements++; - - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it, this), true); - } - - template::value>::type* = nullptr> - std::pair emplace_impl(std::size_t ibucket, typename array_bucket::const_iterator end_of_bucket, - const CharT* key, size_type key_size) - { - if(m_nb_elements >= max_size()) { - THROW(std::length_error, "Can't insert value, too much values in the map."); - } - - auto it = m_buckets[ibucket].append(end_of_bucket, key, key_size); - m_nb_elements++; - - return std::make_pair(iterator(m_buckets_data.begin() + ibucket, it, this), true); - } - - void rehash_impl(size_type bucket_count) { - GrowthPolicy new_growth_policy(bucket_count); - if(bucket_count == this->bucket_count()) { - return; - } - - - if(should_clear_old_erased_values(REHASH_CLEAR_OLD_ERASED_VALUE_THRESHOLD)) { - clear_old_erased_values(); - } - - - std::vector required_size_for_bucket(bucket_count, 0); - std::vector bucket_for_ivalue(size(), 0); - - std::size_t ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - const std::size_t hash = hash_key(it.key(), it.key_size()); - const std::size_t ibucket = new_growth_policy.bucket_for_hash(hash); - - bucket_for_ivalue[ivalue] = ibucket; - required_size_for_bucket[ibucket] += array_bucket::entry_required_bytes(it.key_size()); - ivalue++; - } - - - - - std::vector new_buckets; - new_buckets.reserve(bucket_count); - for(std::size_t ibucket = 0; ibucket < bucket_count; ibucket++) { - new_buckets.emplace_back(required_size_for_bucket[ibucket]); - } - - - ivalue = 0; - for(auto it = begin(); it != end(); ++it) { - const std::size_t ibucket = bucket_for_ivalue[ivalue]; - append_iterator_in_reserved_bucket_no_check(new_buckets[ibucket], it); - - ivalue++; - } - - - using std::swap; - swap(static_cast(*this), new_growth_policy); - - m_buckets_data.swap(new_buckets); - m_buckets = !m_buckets_data.empty()?m_buckets_data.data(): - static_empty_bucket_ptr(); - - // Call max_load_factor to change m_load_threshold - max_load_factor(m_max_load_factor); - } - - template::value>::type* = nullptr> - void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) { - bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size()); - } - - template::value>::type* = nullptr> - void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) { - bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size(), it.value_position()); - } - - - - /** - * On serialization the values of each bucket (if has_mapped_type is true) are serialized - * next to the bucket. The potential old erased values in value_container are thus not serialized. - * - * On deserialization, when hash_compatible is true, we reaffect the value index (IndexSizeT) of each - * bucket with set_value as the position of each value is no more the same in value_container compared - * to when they were serialized. - * - * It's done this way as we can't call clear_old_erased_values() because we want the serialize - * method to remain const and we don't want to serialize/deserialize old erased values. As we may - * not serialize all the values in value_container, the values we keep can change of index. - * We thus have to modify the value indexes in the buckets. - */ - template - void serialize_impl(Serializer& serializer) const { - const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; - serializer(version); - - const slz_size_type bucket_count = m_buckets_data.size(); - serializer(bucket_count); - - const slz_size_type nb_elements = m_nb_elements; - serializer(nb_elements); - - const float max_load_factor = m_max_load_factor; - serializer(max_load_factor); - - for(const array_bucket& bucket: m_buckets_data) { - bucket.serialize(serializer); - serialize_bucket_values(serializer, bucket); - } - } - - template::value>::type* = nullptr> - void serialize_bucket_values(Serializer& /*serializer*/, const array_bucket& /*bucket*/) const { - } - - template::value>::type* = nullptr> - void serialize_bucket_values(Serializer& serializer, const array_bucket& bucket) const { - for(auto it = bucket.begin(); it != bucket.end(); ++it) { - serializer(this->m_values[it.value()]); - } - } - - template - void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { - tsl_ah_assert(m_buckets_data.empty()); // Current hash table must be empty - - const slz_size_type version = deserialize_value(deserializer); - // For now we only have one version of the serialization protocol. - // If it doesn't match there is a problem with the file. - if(version != SERIALIZATION_PROTOCOL_VERSION) { - THROW(std::runtime_error, "Can't deserialize the array_map/set. The protocol version header is invalid."); - } - - const slz_size_type bucket_count_ds = deserialize_value(deserializer); - const slz_size_type nb_elements = deserialize_value(deserializer); - const float max_load_factor = deserialize_value(deserializer); - - - m_nb_elements = numeric_cast(nb_elements, "Deserialized nb_elements is too big."); - - size_type bucket_count = numeric_cast(bucket_count_ds, "Deserialized bucket_count is too big."); - GrowthPolicy::operator=(GrowthPolicy(bucket_count)); - - - this->max_load_factor(max_load_factor); - value_container::reserve(m_nb_elements); - - - if(hash_compatible) { - if(bucket_count != bucket_count_ds) { - THROW(std::runtime_error, "The GrowthPolicy is not the same even though hash_compatible is true."); - } - - m_buckets_data.reserve(bucket_count); - for(size_type i = 0; i < bucket_count; i++) { - m_buckets_data.push_back(array_bucket::deserialize(deserializer)); - deserialize_bucket_values(deserializer, m_buckets_data.back()); - } - } - else { - m_buckets_data.resize(bucket_count); - for(size_type i = 0; i < bucket_count; i++) { - // TODO use buffer to avoid reallocation on each deserialization. - array_bucket bucket = array_bucket::deserialize(deserializer); - deserialize_bucket_values(deserializer, bucket); - - for(auto it_val = bucket.cbegin(); it_val != bucket.cend(); ++it_val) { - const std::size_t ibucket = bucket_for_hash(hash_key(it_val.key(), it_val.key_size())); - - auto it_find = m_buckets_data[ibucket].find_or_end_of_bucket(it_val.key(), it_val.key_size()); - if(it_find.second) { - THROW(std::runtime_error, "Error on deserialization, the same key is presents multiple times."); - } - - append_array_bucket_iterator_in_bucket(m_buckets_data[ibucket], it_find.first, it_val); - } - } - } - - m_buckets = m_buckets_data.data(); - - - if(load_factor() > this->max_load_factor()) { - THROW(std::runtime_error, "Invalid max_load_factor. Check that the serializer and deserializer support " - "floats correctly as they can be converted implicitely to ints."); - } - } - - template::value>::type* = nullptr> - void deserialize_bucket_values(Deserializer& /*deserializer*/, array_bucket& /*bucket*/) { - } - - template::value>::type* = nullptr> - void deserialize_bucket_values(Deserializer& deserializer, array_bucket& bucket) { - for(auto it = bucket.begin(); it != bucket.end(); ++it) { - this->m_values.emplace_back(deserialize_value(deserializer)); - - tsl_ah_assert(this->m_values.size() - 1 <= std::numeric_limits::max()); - it.set_value(static_cast(this->m_values.size() - 1)); - } - } - - template::value>::type* = nullptr> - void append_array_bucket_iterator_in_bucket(array_bucket& bucket, - typename array_bucket::const_iterator end_of_bucket, - typename array_bucket::const_iterator it_val) - { - bucket.append(end_of_bucket, it_val.key(), it_val.key_size()); - } - - template::value>::type* = nullptr> - void append_array_bucket_iterator_in_bucket(array_bucket& bucket, - typename array_bucket::const_iterator end_of_bucket, - typename array_bucket::const_iterator it_val) - { - bucket.append(end_of_bucket, it_val.key(), it_val.key_size(), it_val.value()); - } - -public: - static const size_type DEFAULT_INIT_BUCKET_COUNT = 0; - static constexpr float DEFAULT_MAX_LOAD_FACTOR = 2.0f; - static const size_type MAX_KEY_SIZE = array_bucket::MAX_KEY_SIZE; - -private: - /** - * Protocol version currenlty used for serialization. - */ - static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; - - - static constexpr float DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD = 0.6f; - static constexpr float REHASH_CLEAR_OLD_ERASED_VALUE_THRESHOLD = 0.9f; - - - /** - * Return an always valid pointer to a static empty array_bucket. - */ - array_bucket* static_empty_bucket_ptr() { - static array_bucket empty_bucket; - return &empty_bucket; - } - -private: - std::vector m_buckets_data; - - /** - * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points to static_empty_bucket_ptr. - * This variable is useful to avoid the cost of checking if m_buckets_data is empty when trying - * to find an element. - * - * TODO Remove m_buckets_data and only use a pointer+size instead of a pointer+vector to save some space in the array_hash object. - */ - array_bucket* m_buckets; - - IndexSizeT m_nb_elements; - float m_max_load_factor; - size_type m_load_threshold; -}; - -} // end namespace detail_array_hash -} //end namespace tsl - -#endif diff --git a/ios/include/tsl/array-hash/array_map.h b/ios/include/tsl/array-hash/array_map.h deleted file mode 100644 index bc534bf2..00000000 --- a/ios/include/tsl/array-hash/array_map.h +++ /dev/null @@ -1,863 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_MAP_H -#define TSL_ARRAY_MAP_H - -#include -#include -#include -#include -#include -#include -#include -#include "array_hash.h" - -namespace tsl { - - -/** - * Implementation of a cache-conscious string hash map. - * - * The map stores the strings as `const CharT*`. If `StoreNullTerminator` is true, - * the strings are stored with the a null-terminator (the `key()` method of the iterators - * will return a pointer to this null-terminated string). Otherwise the null character - * is not stored (which allow an economy of 1 byte per string). - * - * The value `T` must be either nothrow move-constructible, copy-constructible or both. - * - * The size of a key string is limited to `std::numeric_limits::max() - 1`. - * That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter. - * See `max_key_size()` for an easy access to this limit. - * - * The number of elements in the map is limited to `std::numeric_limits::max()`. - * That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter. - * See `max_size()` for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - * - shrink_to_fit: always invalidate the iterators. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t, - class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>> -class array_map { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_array_hash::array_hash; - -public: - using char_type = typename ht::char_type; - using mapped_type = T; - using key_size_type = typename ht::key_size_type; - using index_size_type = typename ht::index_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using key_equal = typename ht::key_equal; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - -public: - array_map(): array_map(ht::DEFAULT_INIT_BUCKET_COUNT) { - } - - explicit array_map(size_type bucket_count, - const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR) - { - } - - template::value>::type* = nullptr> - array_map(InputIt first, InputIt last, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(first, last); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_map(std::initializer_list, T>> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(init); - } -#else - array_map(std::initializer_list> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_map(bucket_count, hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_map& operator=(std::initializer_list, T>> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#else - array_map& operator=(std::initializer_list> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, const T& value) { - return m_ht.emplace(key.data(), key.size(), value); - } -#else - std::pair insert(const CharT* key, const T& value) { - return m_ht.emplace(key, std::char_traits::length(key), value); - } - - std::pair insert(const std::basic_string& key, const T& value) { - return m_ht.emplace(key.data(), key.size(), value); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size, const T& value) { - return m_ht.emplace(key, key_size, value); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, T&& value) { - return m_ht.emplace(key.data(), key.size(), std::move(value)); - } -#else - std::pair insert(const CharT* key, T&& value) { - return m_ht.emplace(key, std::char_traits::length(key), std::move(value)); - } - - std::pair insert(const std::basic_string& key, T&& value) { - return m_ht.emplace(key.data(), key.size(), std::move(value)); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size, T&& value) { - return m_ht.emplace(key, key_size, std::move(value)); - } - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - if(std::is_base_of::iterator_category>::value) - { - const auto nb_elements_insert = std::distance(first, last); - const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size(); - - if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) { - reserve(size() + std::size_t(nb_elements_insert)); - } - } - - for(auto it = first; it != last; ++it) { - insert_pair(*it); - } - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - void insert(std::initializer_list, T>> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - template - std::pair insert_or_assign(const std::basic_string_view& key, M&& obj) { - return m_ht.insert_or_assign(key.data(), key.size(), std::forward(obj)); - } -#else - template - std::pair insert_or_assign(const CharT* key, M&& obj) { - return m_ht.insert_or_assign(key, std::char_traits::length(key), std::forward(obj)); - } - - template - std::pair insert_or_assign(const std::basic_string& key, M&& obj) { - return m_ht.insert_or_assign(key.data(), key.size(), std::forward(obj)); - } -#endif - template - std::pair insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) { - return m_ht.insert_or_assign(key, key_size, std::forward(obj)); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - template - std::pair emplace(const std::basic_string_view& key, Args&&... args) { - return m_ht.emplace(key.data(), key.size(), std::forward(args)...); - } -#else - template - std::pair emplace(const CharT* key, Args&&... args) { - return m_ht.emplace(key, std::char_traits::length(key), std::forward(args)...); - } - - template - std::pair emplace(const std::basic_string& key, Args&&... args) { - return m_ht.emplace(key.data(), key.size(), std::forward(args)...); - } -#endif - template - std::pair emplace_ks(const CharT* key, size_type key_size, Args&&... args) { - return m_ht.emplace(key, key_size, std::forward(args)...); - } - - - - /** - * Erase has an amortized O(1) runtime complexity, but even if it removes the key immediately, - * it doesn't do the same for the associated value T. - * - * T will only be removed when the ratio between the size of the map and - * the size of the map + the number of deleted values still stored is low enough. - * - * To force the deletion you can call shrink_to_fit. - */ - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - - /** - * @copydoc erase(const_iterator pos) - */ - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const CharT* key) { - return m_ht.erase(key, std::char_traits::length(key)); - } - - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - /** - * @copydoc erase(const_iterator pos) - */ - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const CharT* key, std::size_t precalculated_hash) { - return m_ht.erase(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * @copydoc erase(const_iterator pos) - * - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.erase(key, key_size, precalculated_hash); - } - - - - void swap(array_map& other) { other.m_ht.swap(m_ht); } - - - - /* - * Lookup - */ -#ifdef TSL_AH_HAS_STRING_VIEW - T& at(const std::basic_string_view& key) { - return m_ht.at(key.data(), key.size()); - } - - const T& at(const std::basic_string_view& key) const { - return m_ht.at(key.data(), key.size()); - } -#else - T& at(const CharT* key) { - return m_ht.at(key, std::char_traits::length(key)); - } - - const T& at(const CharT* key) const { - return m_ht.at(key, std::char_traits::length(key)); - } - - T& at(const std::basic_string& key) { - return m_ht.at(key.data(), key.size()); - } - - const T& at(const std::basic_string& key) const { - return m_ht.at(key.data(), key.size()); - } -#endif - T& at_ks(const CharT* key, size_type key_size) { - return m_ht.at(key, key_size); - } - - const T& at_ks(const CharT* key, size_type key_size) const { - return m_ht.at(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const CharT* key, std::size_t precalculated_hash) { - return m_ht.at(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.at(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - T& at(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.at(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.at(key, key_size, precalculated_hash); - } - - /** - * @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.at(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - T& operator[](const std::basic_string_view& key) { return m_ht.access_operator(key.data(), key.size()); } -#else - T& operator[](const CharT* key) { return m_ht.access_operator(key, std::char_traits::length(key)); } - T& operator[](const std::basic_string& key) { return m_ht.access_operator(key.data(), key.size()); } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { - return m_ht.count(key.data(), key.size()); - } -#else - size_type count(const CharT* key) const { - return m_ht.count(key, std::char_traits::length(key)); - } - - size_type count(const std::basic_string& key) const { - return m_ht.count(key.data(), key.size()); - } -#endif - size_type count_ks(const CharT* key, size_type key_size) const { - return m_ht.count(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.count(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.count(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::char_traits::length(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::char_traits::length(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const CharT* key, std::size_t precalculated_hash) { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.find(key, key_size, precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.find(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - - - /* - * Bucket interface - */ - size_type bucket_count() const { return m_ht.bucket_count(); } - size_type max_bucket_count() const { return m_ht.max_bucket_count(); } - - - /* - * Hash policy - */ - float load_factor() const { return m_ht.load_factor(); } - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - void rehash(size_type count) { m_ht.rehash(count); } - void reserve(size_type count) { m_ht.reserve(count); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - key_equal key_eq() const { return m_ht.key_eq(); } - - - /* - * Other - */ - /** - * Return the `const_iterator it` as an `iterator`. - */ - iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); } - - /** - * Serialize the map through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `template void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - /** - * Deserialize a previously serialized map through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be - * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the - * serialized map. Otherwise the behaviour is undefined with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` and `T` of the `array_map` are not the same as the - * types used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static array_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { - array_map map(0); - map.m_ht.deserialize(deserializer, hash_compatible); - - return map; - } - - friend bool operator==(const array_map& lhs, const array_map& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size()); - if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const array_map& lhs, const array_map& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(array_map& lhs, array_map& rhs) { - lhs.swap(rhs); - } - -private: - template - void insert_pair(const std::pair& value) { - insert(value.first, value.second); - } - - template - void insert_pair(std::pair&& value) { - insert(value.first, std::move(value.second)); - } - -public: - static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE; - -private: - ht m_ht; -}; - - -/** - * Same as - * `tsl::array_map`. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t> -using array_pg_map = array_map; - -} //end namespace tsl - -#endif diff --git a/ios/include/tsl/array-hash/array_set.h b/ios/include/tsl/array-hash/array_set.h deleted file mode 100644 index 0322bcd0..00000000 --- a/ios/include/tsl/array-hash/array_set.h +++ /dev/null @@ -1,664 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_ARRAY_SET_H -#define TSL_ARRAY_SET_H - -#include -#include -#include -#include -#include -#include -#include -#include "array_hash.h" - -namespace tsl { - -/** - * Implementation of a cache-conscious string hash set. - * - * The set stores the strings as `const CharT*`. If `StoreNullTerminator` is true, - * the strings are stored with the a null-terminator (the `key()` method of the iterators - * will return a pointer to this null-terminated string). Otherwise the null character - * is not stored (which allow an economy of 1 byte per string). - * - * The size of a key string is limited to `std::numeric_limits::max() - 1`. - * That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter. - * See `max_key_size()` for an easy access to this limit. - * - * The number of elements in the set is limited to `std::numeric_limits::max()`. - * That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter. - * See `max_size()` for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - * - shrink_to_fit: always invalidate the iterators. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t, - class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>> -class array_set { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_array_hash::array_hash; - -public: - using char_type = typename ht::char_type; - using key_size_type = typename ht::key_size_type; - using index_size_type = typename ht::index_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using key_equal = typename ht::key_equal; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - - array_set(): array_set(ht::DEFAULT_INIT_BUCKET_COUNT) { - } - - explicit array_set(size_type bucket_count, - const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR) - { - } - - template::value>::type* = nullptr> - array_set(InputIt first, InputIt last, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(first, last); - } - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_set(std::initializer_list> init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(init); - } -#else - array_set(std::initializer_list init, - size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT, - const Hash& hash = Hash()): array_set(bucket_count, hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - array_set& operator=(std::initializer_list> ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#else - array_set& operator=(std::initializer_list ilist) { - clear(); - - reserve(ilist.size()); - insert(ilist); - - return *this; - } -#endif - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key) { - return m_ht.emplace(key.data(), key.size()); - } -#else - std::pair insert(const CharT* key) { - return m_ht.emplace(key, std::char_traits::length(key)); - } - - std::pair insert(const std::basic_string& key) { - return m_ht.emplace(key.data(), key.size()); - } -#endif - std::pair insert_ks(const CharT* key, size_type key_size) { - return m_ht.emplace(key, key_size); - } - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - if(std::is_base_of::iterator_category>::value) - { - const auto nb_elements_insert = std::distance(first, last); - const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size(); - - if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) { - reserve(size() + std::size_t(nb_elements_insert)); - } - } - - for(auto it = first; it != last; ++it) { - insert(*it); - } - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const std::basic_string_view& key) { - return m_ht.emplace(key.data(), key.size()); - } -#else - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const CharT* key) { - return m_ht.emplace(key, std::char_traits::length(key)); - } - - /** - * @copydoc emplace_ks(const CharT* key, size_type key_size) - */ - std::pair emplace(const std::basic_string& key) { - return m_ht.emplace(key.data(), key.size()); - } -#endif - /** - * No difference compared to the insert method. Mainly here for coherence with array_map. - */ - std::pair emplace_ks(const CharT* key, size_type key_size) { - return m_ht.emplace(key, key_size); - } - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - -#ifdef TSL_AH_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::char_traits::length(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const CharT* key, std::size_t precalculated_hash) { - return m_ht.erase(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - size_type erase(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.erase(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.erase(key, key_size, precalculated_hash); - } - - - - void swap(array_set& other) { other.m_ht.swap(m_ht); } - - - - /* - * Lookup - */ -#ifdef TSL_AH_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::char_traits::length(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.count(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const - */ - size_type count(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.count(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.count(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::char_traits::length(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::char_traits::length(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const CharT* key, std::size_t precalculated_hash) { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.find(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - iterator find(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.find(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.find(key, key_size, precalculated_hash); - } - - /** - * @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.find(key, key_size, precalculated_hash); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::char_traits::length(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } - - - -#ifdef TSL_AH_HAS_STRING_VIEW - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string_view& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#else - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const CharT* key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, std::char_traits::length(key), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range(const std::basic_string& key, std::size_t precalculated_hash) const { - return m_ht.equal_range(key.data(), key.size(), precalculated_hash); - } -#endif - /** - * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same - * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - /** - * @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) - */ - std::pair equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const { - return m_ht.equal_range(key, key_size, precalculated_hash); - } - - - - /* - * Bucket interface - */ - size_type bucket_count() const { return m_ht.bucket_count(); } - size_type max_bucket_count() const { return m_ht.max_bucket_count(); } - - - /* - * Hash policy - */ - float load_factor() const { return m_ht.load_factor(); } - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - void rehash(size_type count) { m_ht.rehash(count); } - void reserve(size_type count) { m_ht.reserve(count); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - key_equal key_eq() const { return m_ht.key_eq(); } - - - /* - * Other - */ - /** - * Return the `const_iterator it` as an `iterator`. - */ - iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); } - - /** - * Serialize the set through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `template void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - /** - * Deserialize a previously serialized set through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be - * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the - * serialized set. Otherwise the behaviour is undefined with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` of the `array_set` is not the same as the - * type used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static array_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { - array_set set(0); - set.m_ht.deserialize(deserializer, hash_compatible); - - return set; - } - - friend bool operator==(const array_set& lhs, const array_set& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size()); - if(it_element_rhs == rhs.cend()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const array_set& lhs, const array_set& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(array_set& lhs, array_set& rhs) { - lhs.swap(rhs); - } - -public: - static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE; - -private: - ht m_ht; -}; - - -/** - * Same as - * `tsl::array_set`. - */ -template, - class KeyEqual = tsl::ah::str_equal, - bool StoreNullTerminator = true, - class KeySizeT = std::uint16_t, - class IndexSizeT = std::uint32_t> -using array_pg_set = array_set; - -} //end namespace tsl - -#endif diff --git a/ios/include/tsl/htrie_hash.h b/ios/include/tsl/htrie_hash.h deleted file mode 100644 index 99ee0724..00000000 --- a/ios/include/tsl/htrie_hash.h +++ /dev/null @@ -1,2090 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_HASH_H -#define TSL_HTRIE_HASH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array-hash/array_map.h" -#include "array-hash/array_set.h" - - -/* - * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433), - * check also __cplusplus version. - */ -#ifdef __has_include -# if __has_include() && __cplusplus >= 201703L -# define TSL_HT_HAS_STRING_VIEW -# endif -#endif - - -#ifdef TSL_HT_HAS_STRING_VIEW -# include -#endif - - -#ifdef TSL_DEBUG -# define tsl_ht_assert(expr) assert(expr) -#else -# define tsl_ht_assert(expr) (static_cast(0)) -#endif - - -namespace tsl { - -namespace detail_htrie_hash { - -template -struct is_iterator: std::false_type { -}; - -template -struct is_iterator::iterator_category, void>::value>::type>: std::true_type { -}; - -template -struct is_related: std::false_type {}; - -template -struct is_related: std::is_same::type>::type, - typename std::remove_cv::type>::type> {}; - -template -static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { - T ret = static_cast(value); - if(static_cast(ret) != value) { - THROW(std::runtime_error, error_message); - } - - const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || - (std::is_signed::value && std::is_signed::value); - if(!is_same_signedness && (ret < T{}) != (value < U{})) { - THROW(std::runtime_error, error_message); - } - - return ret; -} - - -template -struct value_node { - /* - * Avoid conflict with copy constructor 'value_node(const value_node&)'. If we call the copy constructor - * with a mutable reference 'value_node(value_node&)', we don't want the forward constructor to be called. - */ - template::value>::type* = nullptr> - value_node(Args&&... args): m_value(std::forward(args)...) { - } - - T m_value; -}; - -template<> -struct value_node { -}; - - -/** - * T should be void if there is no value associated to a key (in a set for example). - */ -template -class htrie_hash { -private: - template - using has_value = typename std::integral_constant::value>; - - static_assert(std::is_same::value, "char is the only supported CharT type for now."); - - static const std::size_t ALPHABET_SIZE = - std::numeric_limits::type>::max() + 1; - - -public: - template - class htrie_hash_iterator; - - - using char_type = CharT; - using key_size_type = KeySizeT; - using size_type = std::size_t; - using hasher = Hash; - using iterator = htrie_hash_iterator; - using const_iterator = htrie_hash_iterator; - using prefix_iterator = htrie_hash_iterator; - using const_prefix_iterator = htrie_hash_iterator; - - -private: - using array_hash_type = - typename std::conditional< - has_value::value, - tsl::array_map, false, - KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>, - tsl::array_set, false, - KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>>::type; - - -private: - /* - * The tree is mainly composed of two nodes types: trie_node and hash_node which both have anode as base class. - * Each child is either a hash_node or a trie_node. - * - * A hash_node is always a leaf node, it doesn't have any child. - * - * Example: - * | ... | a |.. ..................... | f | ... | trie_node_1 - * \ \ - * hash_node_1 |array_hash = {"dd"}| |...| a | ... | trie_node_2 - * / - * |array_hash = {"ble", "bric", "lse"}| hash_node_2 - * - * - * Each trie_node may also have a value node, which contains a value T, if the trie_node marks - * the end of a string value. - * - * A trie node should at least have one child or a value node. There can't be a trie node without - * any child and no value node. - */ - - using value_node = tsl::detail_htrie_hash::value_node; - - - class trie_node; - class hash_node; - - // TODO better encapsulate operations modifying the tree. - class anode { - friend class trie_node; - - public: - /* - * TODO Avoid the virtual to economize 8 bytes. We could use a custom deleter in the std::unique_ptr - * we use (as we know if an anode is a trie_node or hash_node). - */ - virtual ~anode() = default; - - bool is_trie_node() const noexcept { - return m_node_type == node_type::TRIE_NODE; - } - - bool is_hash_node() const noexcept { - return m_node_type == node_type::HASH_NODE; - } - - trie_node& as_trie_node() noexcept { - tsl_ht_assert(is_trie_node()); - return static_cast(*this); - } - - hash_node& as_hash_node() noexcept { - tsl_ht_assert(is_hash_node()); - return static_cast(*this); - } - - const trie_node& as_trie_node() const noexcept { - tsl_ht_assert(is_trie_node()); - return static_cast(*this); - } - - const hash_node& as_hash_node() const noexcept { - tsl_ht_assert(is_hash_node()); - return static_cast(*this); - } - - /** - * @see m_child_of_char - */ - CharT child_of_char() const noexcept { - tsl_ht_assert(parent() != nullptr); - return m_child_of_char; - } - - /** - * Return nullptr if none. - */ - trie_node* parent() noexcept { - return m_parent_node; - } - - const trie_node* parent() const noexcept { - return m_parent_node; - } - - protected: - enum class node_type: unsigned char { - HASH_NODE, - TRIE_NODE - }; - - anode(node_type node_type_): m_node_type(node_type_), m_child_of_char(0), - m_parent_node(nullptr) - { - } - - anode(node_type node_type_, CharT child_of_char): m_node_type(node_type_), - m_child_of_char(child_of_char), - m_parent_node(nullptr) - { - } - - - protected: - node_type m_node_type; - - /** - * If the node has a parent, then it's a descendant of some char. - * - * Example: - * | ... | a | b | ... | trie_node_1 - * \ - * |...| a | ... | trie_node_2 - * / - * |array_hash| hash_node_1 - * - * trie_node_2 is a child of trie_node_1 through 'b', it will have 'b' as m_child_of_char. - * hash_node_1 is a child of trie_node_2 through 'a', it will have 'a' as m_child_of_char. - * - * trie_node_1 has no parent, its m_child_of_char is undefined. - */ - CharT m_child_of_char; - trie_node* m_parent_node; - }; - - // Give the position in trie_node::m_children corresponding to the character c - static std::size_t as_position(CharT c) noexcept { - return static_cast(static_cast::type>(c)); - } - - class trie_node: public anode { - public: - trie_node(): anode(anode::node_type::TRIE_NODE), - m_value_node(nullptr), m_children() - { - } - - trie_node(const trie_node& other): anode(anode::node_type::TRIE_NODE, other.m_child_of_char), - m_value_node(nullptr), m_children() - { - if(other.m_value_node != nullptr) { - m_value_node = make_unique(*other.m_value_node); - } - - // TODO avoid recursion - for(std::size_t ichild = 0; ichild < other.m_children.size(); ichild++) { - if(other.m_children[ichild] != nullptr) { - if(other.m_children[ichild]->is_hash_node()) { - m_children[ichild] = make_unique(other.m_children[ichild]->as_hash_node()); - } - else { - m_children[ichild] = make_unique(other.m_children[ichild]->as_trie_node()); - } - - m_children[ichild]->m_parent_node = this; - } - } - } - - trie_node(trie_node&& other) = delete; - trie_node& operator=(const trie_node& other) = delete; - trie_node& operator=(trie_node&& other) = delete; - - /** - * Return nullptr if none. - */ - anode* first_child() noexcept { - return const_cast(static_cast(this)->first_child()); - } - - const anode* first_child() const noexcept { - for(std::size_t ichild = 0; ichild < m_children.size(); ichild++) { - if(m_children[ichild] != nullptr) { - return m_children[ichild].get(); - } - } - - return nullptr; - } - - - /** - * Get the next_child that come after current_child. Return nullptr if no next child. - */ - anode* next_child(const anode& current_child) noexcept { - return const_cast(static_cast(this)->next_child(current_child)); - } - - const anode* next_child(const anode& current_child) const noexcept { - tsl_ht_assert(current_child.parent() == this); - - for(std::size_t ichild = as_position(current_child.child_of_char()) + 1; - ichild < m_children.size(); - ichild++) - { - if(m_children[ichild] != nullptr) { - return m_children[ichild].get(); - } - } - - return nullptr; - } - - - /** - * Return the first left-descendant trie node with an m_value_node. If none return the most left trie node. - */ - trie_node& most_left_descendant_value_trie_node() noexcept { - return const_cast(static_cast(this)->most_left_descendant_value_trie_node()); - } - - const trie_node& most_left_descendant_value_trie_node() const noexcept { - const trie_node* current_node = this; - while(true) { - if(current_node->m_value_node != nullptr) { - return *current_node; - } - - const anode* first_child = current_node->first_child(); - tsl_ht_assert(first_child != nullptr); // a trie_node must either have a value_node or at least one child. - if(first_child->is_hash_node()) { - return *current_node; - } - - current_node = &first_child->as_trie_node(); - } - } - - - - size_type nb_children() const noexcept { - return std::count_if(m_children.cbegin(), m_children.cend(), - [](const std::unique_ptr& n) { return n != nullptr; }); - } - - bool empty() const noexcept { - return std::all_of(m_children.cbegin(), m_children.cend(), - [](const std::unique_ptr& n) { return n == nullptr; }); - } - - std::unique_ptr& child(CharT for_char) noexcept { - return m_children[as_position(for_char)]; - } - - const std::unique_ptr& child(CharT for_char) const noexcept { - return m_children[as_position(for_char)]; - } - - typename std::array, ALPHABET_SIZE>::iterator begin() noexcept { - return m_children.begin(); - } - - typename std::array, ALPHABET_SIZE>::iterator end() noexcept { - return m_children.end(); - } - - void set_child(CharT for_char, std::unique_ptr child) noexcept { - if(child != nullptr) { - child->m_child_of_char = for_char; - child->m_parent_node = this; - } - - m_children[as_position(for_char)] = std::move(child); - } - - std::unique_ptr& val_node() noexcept { - return m_value_node; - } - - const std::unique_ptr& val_node() const noexcept { - return m_value_node; - } - - private: - // TODO Avoid storing a value_node when has_value::value is false - std::unique_ptr m_value_node; - - /** - * Each character CharT corresponds to one position in the array. To convert a character - * to a position use the as_position method. - * - * TODO Try to reduce the size of m_children with a hash map, linear/binary search on array, ... - * TODO Store number of non-null values in m_children. Check if we can store this value in the alignment - * space as we don't want the node to get bigger (empty() and nb_children() are rarely used so it is - * not an important variable). - */ - std::array, ALPHABET_SIZE> m_children; - }; - - - class hash_node: public anode { - public: - hash_node(const Hash& hash, float max_load_factor): - hash_node(HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT, hash, max_load_factor) - { - } - - hash_node(size_type bucket_count, const Hash& hash, float max_load_factor): - anode(anode::node_type::HASH_NODE), m_array_hash(bucket_count, hash) - { - m_array_hash.max_load_factor(max_load_factor); - } - - hash_node(array_hash_type&& array_hash) noexcept(std::is_nothrow_move_constructible::value): - anode(anode::node_type::HASH_NODE), m_array_hash(std::move(array_hash)) - { - } - - hash_node(const hash_node& other) = default; - - hash_node(hash_node&& other) = delete; - hash_node& operator=(const hash_node& other) = delete; - hash_node& operator=(hash_node&& other) = delete; - - - array_hash_type& array_hash() noexcept { - return m_array_hash; - } - - const array_hash_type& array_hash() const noexcept { - return m_array_hash; - } - - private: - array_hash_type m_array_hash; - }; - - - -public: - template - class htrie_hash_iterator { - friend class htrie_hash; - - private: - using anode_type = typename std::conditional::type; - using trie_node_type = typename std::conditional::type; - using hash_node_type = typename std::conditional::type; - - using array_hash_iterator_type = - typename std::conditional::type; - - public: - using iterator_category = std::forward_iterator_tag; - using value_type = typename std::conditional::value, T, void>::type; - using difference_type = std::ptrdiff_t; - using reference = typename std::conditional< - has_value::value, - typename std::conditional::type, - typename std::add_lvalue_reference::type>::type, - void>::type; - using pointer = typename std::conditional< - has_value::value, - typename std::conditional::type, - void>::type; - - private: - /** - * Start reading from start_hash_node->array_hash().begin(). - */ - htrie_hash_iterator(hash_node_type& start_hash_node) noexcept: - htrie_hash_iterator(start_hash_node, start_hash_node.array_hash().begin()) - { - } - - /** - * Start reading from iterator begin in start_hash_node->array_hash(). - */ - htrie_hash_iterator(hash_node_type& start_hash_node, array_hash_iterator_type begin) noexcept: - m_current_trie_node(start_hash_node.parent()), m_current_hash_node(&start_hash_node), - m_array_hash_iterator(begin), - m_array_hash_end_iterator(start_hash_node.array_hash().end()), - m_read_trie_node_value(false) - { - tsl_ht_assert(!m_current_hash_node->array_hash().empty()); - } - - /** - * Start reading from the value in start_trie_node. start_trie_node->val_node() should be non-null. - */ - htrie_hash_iterator(trie_node_type& start_trie_node) noexcept: - m_current_trie_node(&start_trie_node), m_current_hash_node(nullptr), - m_read_trie_node_value(true) - { - tsl_ht_assert(m_current_trie_node->val_node() != nullptr); - } - - template::type* = nullptr> - htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, - array_hash_iterator_type begin, array_hash_iterator_type end, - bool read_trie_node_value) noexcept: - m_current_trie_node(tnode), m_current_hash_node(hnode), - m_array_hash_iterator(begin), m_array_hash_end_iterator(end), - m_read_trie_node_value(read_trie_node_value) - { - } - - template::type* = nullptr> - htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, - array_hash_iterator_type begin, array_hash_iterator_type end, - bool read_trie_node_value, std::basic_string prefix_filter) noexcept: - m_current_trie_node(tnode), m_current_hash_node(hnode), - m_array_hash_iterator(begin), m_array_hash_end_iterator(end), - m_read_trie_node_value(read_trie_node_value), m_prefix_filter(std::move(prefix_filter)) - { - } - - public: - htrie_hash_iterator() noexcept { - } - - // Copy constructor from iterator to const_iterator. - template::type* = nullptr> - htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: - m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), - m_array_hash_iterator(other.m_array_hash_iterator), - m_array_hash_end_iterator(other.m_array_hash_end_iterator), - m_read_trie_node_value(other.m_read_trie_node_value) - { - } - - // Copy constructor from iterator to const_iterator. - template::type* = nullptr> - htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: - m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), - m_array_hash_iterator(other.m_array_hash_iterator), - m_array_hash_end_iterator(other.m_array_hash_end_iterator), - m_read_trie_node_value(other.m_read_trie_node_value), m_prefix_filter(other.m_prefix_filter) - { - } - - htrie_hash_iterator(const htrie_hash_iterator& other) = default; - htrie_hash_iterator(htrie_hash_iterator&& other) = default; - htrie_hash_iterator& operator=(const htrie_hash_iterator& other) = default; - htrie_hash_iterator& operator=(htrie_hash_iterator&& other) = default; - - void key(std::basic_string& key_buffer_out) const { - key_buffer_out.clear(); - - trie_node_type* tnode = m_current_trie_node; - while(tnode != nullptr && tnode->parent() != nullptr) { - key_buffer_out.push_back(tnode->child_of_char()); - tnode = tnode->parent(); - } - - std::reverse(key_buffer_out.begin(), key_buffer_out.end()); - - if(!m_read_trie_node_value) { - tsl_ht_assert(m_current_hash_node != nullptr); - if(m_current_hash_node->parent() != nullptr) { - key_buffer_out.push_back(m_current_hash_node->child_of_char()); - } - - key_buffer_out.append(m_array_hash_iterator.key(), m_array_hash_iterator.key_size()); - } - } - - std::basic_string key() const { - std::basic_string key_buffer; - key(key_buffer); - - return key_buffer; - } - - template::value>::type* = nullptr> - reference value() const { - if(this->m_read_trie_node_value) { - tsl_ht_assert(this->m_current_trie_node != nullptr); - tsl_ht_assert(this->m_current_trie_node->val_node() != nullptr); - - return this->m_current_trie_node->val_node()->m_value; - } - else { - return this->m_array_hash_iterator.value(); - } - } - - template::value>::type* = nullptr> - reference operator*() const { - return value(); - } - - template::value>::type* = nullptr> - pointer operator->() const { - return std::addressof(value()); - } - - htrie_hash_iterator& operator++() { - if(m_read_trie_node_value) { - tsl_ht_assert(m_current_trie_node != nullptr); - - m_read_trie_node_value = false; - - anode_type* child = m_current_trie_node->first_child(); - if(child != nullptr) { - set_most_left_descendant_as_next_node(*child); - } - else if(m_current_trie_node->parent() != nullptr) { - trie_node_type* current_node_child = m_current_trie_node; - m_current_trie_node = m_current_trie_node->parent(); - - set_next_node_ascending(*current_node_child); - } - else { - set_as_end_iterator(); - } - } - else { - ++m_array_hash_iterator; - if(m_array_hash_iterator != m_array_hash_end_iterator) { - filter_prefix(); - } - // End of the road, set the iterator as an end node. - else if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - } - - - return *this; - } - - htrie_hash_iterator operator++(int) { - htrie_hash_iterator tmp(*this); - ++*this; - - return tmp; - } - - friend bool operator==(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { - if(lhs.m_current_trie_node != rhs.m_current_trie_node || - lhs.m_read_trie_node_value != rhs.m_read_trie_node_value) - { - return false; - } - else if(lhs.m_read_trie_node_value) { - return true; - } - else { - if(lhs.m_current_hash_node != rhs.m_current_hash_node) { - return false; - } - else if(lhs.m_current_hash_node == nullptr) { - return true; - } - else { - return lhs.m_array_hash_iterator == rhs.m_array_hash_iterator && - lhs.m_array_hash_end_iterator == rhs.m_array_hash_end_iterator; - } - } - } - - friend bool operator!=(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { - return !(lhs == rhs); - } - - private: - void hash_node_prefix(std::basic_string& key_buffer_out) const { - tsl_ht_assert(!m_read_trie_node_value); - key_buffer_out.clear(); - - trie_node_type* tnode = m_current_trie_node; - while(tnode != nullptr && tnode->parent() != nullptr) { - key_buffer_out.push_back(tnode->child_of_char()); - tnode = tnode->parent(); - } - - std::reverse(key_buffer_out.begin(), key_buffer_out.end()); - - tsl_ht_assert(m_current_hash_node != nullptr); - if(m_current_hash_node->parent() != nullptr) { - key_buffer_out.push_back(m_current_hash_node->child_of_char()); - } - } - - template::type* = nullptr> - void filter_prefix() { - } - - template::type* = nullptr> - void filter_prefix() { - tsl_ht_assert(m_array_hash_iterator != m_array_hash_end_iterator); - tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); - - if(m_prefix_filter.empty()) { - return; - } - - while((m_prefix_filter.size() > m_array_hash_iterator.key_size() || - m_prefix_filter.compare(0, m_prefix_filter.size(), - m_array_hash_iterator.key(), m_prefix_filter.size()) != 0)) - { - ++m_array_hash_iterator; - if(m_array_hash_iterator == m_array_hash_end_iterator) { - if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - - return; - } - } - } - - /** - * Go back up in the tree to get the current_trie_node_child sibling. - * If none, try to go back up more in the tree to check the siblings of the ancestors. - */ - void set_next_node_ascending(anode_type& current_trie_node_child) { - tsl_ht_assert(m_current_trie_node != nullptr); - tsl_ht_assert(current_trie_node_child.parent() == m_current_trie_node); - - anode_type* next_node = m_current_trie_node->next_child(current_trie_node_child); - while(next_node == nullptr && m_current_trie_node->parent() != nullptr) { - anode_type* current_child = m_current_trie_node; - m_current_trie_node = m_current_trie_node->parent(); - next_node = m_current_trie_node->next_child(*current_child); - } - - // End of the road, set the iterator as an end node. - if(next_node == nullptr) { - set_as_end_iterator(); - } - else { - set_most_left_descendant_as_next_node(*next_node); - } - } - - void set_most_left_descendant_as_next_node(anode_type& search_start) { - if(search_start.is_hash_node()) { - set_current_hash_node(search_start.as_hash_node()); - } - else { - m_current_trie_node = &search_start.as_trie_node().most_left_descendant_value_trie_node(); - if(m_current_trie_node->val_node() != nullptr) { - m_read_trie_node_value = true; - } - else { - anode_type* first_child = m_current_trie_node->first_child(); - // a trie_node must either have a value_node or at least one child. - tsl_ht_assert(first_child != nullptr); - - set_current_hash_node(first_child->as_hash_node()); - } - } - } - - void set_current_hash_node(hash_node_type& hnode) { - tsl_ht_assert(!hnode.array_hash().empty()); - - m_current_hash_node = &hnode; - m_array_hash_iterator = m_current_hash_node->array_hash().begin(); - m_array_hash_end_iterator = m_current_hash_node->array_hash().end(); - } - - void set_as_end_iterator() { - m_current_trie_node = nullptr; - m_current_hash_node = nullptr; - m_read_trie_node_value = false; - } - - void skip_hash_node() { - tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); - if(m_current_trie_node == nullptr) { - set_as_end_iterator(); - } - else { - tsl_ht_assert(m_current_hash_node != nullptr); - set_next_node_ascending(*m_current_hash_node); - } - } - - private: - trie_node_type* m_current_trie_node; - hash_node_type* m_current_hash_node; - - array_hash_iterator_type m_array_hash_iterator; - array_hash_iterator_type m_array_hash_end_iterator; - - bool m_read_trie_node_value; - // TODO can't have void if !IsPrefixIterator, use inheritance - typename std::conditional, bool>::type m_prefix_filter; - }; - - - -public: - htrie_hash(const Hash& hash, float max_load_factor, size_type burst_threshold): - m_root(nullptr), m_nb_elements(0), - m_hash(hash), m_max_load_factor(max_load_factor) - { - this->burst_threshold(burst_threshold); - } - - htrie_hash(const htrie_hash& other): m_root(nullptr), m_nb_elements(other.m_nb_elements), - m_hash(other.m_hash), m_max_load_factor(other.m_max_load_factor), - m_burst_threshold(other.m_burst_threshold) - { - if(other.m_root != nullptr) { - if(other.m_root->is_hash_node()) { - m_root = make_unique(other.m_root->as_hash_node()); - } - else { - m_root = make_unique(other.m_root->as_trie_node()); - } - } - } - - htrie_hash(htrie_hash&& other) noexcept(std::is_nothrow_move_constructible::value) - : m_root(std::move(other.m_root)), - m_nb_elements(other.m_nb_elements), - m_hash(std::move(other.m_hash)), - m_max_load_factor(other.m_max_load_factor), - m_burst_threshold(other.m_burst_threshold) - { - other.clear(); - } - - htrie_hash& operator=(const htrie_hash& other) { - if(&other != this) { - std::unique_ptr new_root = nullptr; - if(other.m_root != nullptr) { - if(other.m_root->is_hash_node()) { - new_root = make_unique(other.m_root->as_hash_node()); - } - else { - new_root = make_unique(other.m_root->as_trie_node()); - } - } - - m_hash = other.m_hash; - m_root = std::move(new_root); - m_nb_elements = other.m_nb_elements; - m_max_load_factor = other.m_max_load_factor; - m_burst_threshold = other.m_burst_threshold; - } - - return *this; - } - - htrie_hash& operator=(htrie_hash&& other) { - other.swap(*this); - other.clear(); - - return *this; - } - - /* - * Iterators - */ - iterator begin() noexcept { - return mutable_iterator(cbegin()); - } - - const_iterator begin() const noexcept { - return cbegin(); - } - - const_iterator cbegin() const noexcept { - if(empty()) { - return cend(); - } - - return cbegin(*m_root); - } - - iterator end() noexcept { - iterator it; - it.set_as_end_iterator(); - - return it; - } - - const_iterator end() const noexcept { - return cend(); - } - - const_iterator cend() const noexcept { - const_iterator it; - it.set_as_end_iterator(); - - return it; - } - - - /* - * Capacity - */ - bool empty() const noexcept { - return m_nb_elements == 0; - } - - size_type size() const noexcept { - return m_nb_elements; - } - - size_type max_size() const noexcept { - return std::numeric_limits::max(); - } - - size_type max_key_size() const noexcept { - return array_hash_type::MAX_KEY_SIZE; - } - - void shrink_to_fit() { - auto first = begin(); - auto last = end(); - - while(first != last) { - if(first.m_read_trie_node_value) { - ++first; - } - else { - /* - * shrink_to_fit on array_hash will invalidate the iterators of array_hash. - * Save pointer to array_hash, skip the array_hash_node and then call - * shrink_to_fit on the saved pointer. - */ - hash_node* hnode = first.m_current_hash_node; - first.skip_hash_node(); - - tsl_ht_assert(hnode != nullptr); - hnode->array_hash().shrink_to_fit(); - } - } - } - - - /* - * Modifiers - */ - void clear() noexcept { - m_root.reset(nullptr); - m_nb_elements = 0; - } - - template - std::pair insert(const CharT* key, size_type key_size, ValueArgs&&... value_args) { - if(key_size > max_key_size()) { - THROW(std::length_error, "Key is too long."); - } - - if(m_root == nullptr) { - m_root = make_unique(m_hash, m_max_load_factor); - } - - return insert_impl(*m_root, key, key_size, std::forward(value_args)...); - } - - iterator erase(const_iterator pos) { - return erase(mutable_iterator(pos)); - } - - iterator erase(const_iterator first, const_iterator last) { - // TODO Optimize, could avoid the call to std::distance - const std::size_t nb_to_erase = std::size_t(std::distance(first, last)); - auto to_delete = mutable_iterator(first); - for(std::size_t i = 0; i < nb_to_erase; i++) { - to_delete = erase(to_delete); - } - - return to_delete; - } - - size_type erase(const CharT* key, size_type key_size) { - auto it = find(key, key_size); - if(it != end()) { - erase(it); - return 1; - } - else { - return 0; - } - - } - - size_type erase_prefix(const CharT* prefix, size_type prefix_size) { - if(m_root == nullptr) { - return 0; - } - - anode* current_node = m_root.get(); - for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->is_trie_node()) { - trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(prefix[iprefix]) == nullptr) { - return 0; - } - else { - current_node = tnode->child(prefix[iprefix]).get(); - } - } - else { - hash_node& hnode = current_node->as_hash_node(); - return erase_prefix_hash_node(hnode, prefix + iprefix, prefix_size - iprefix); - } - } - - - if(current_node->is_trie_node()) { - trie_node* parent = current_node->parent(); - - if(parent != nullptr) { - const size_type nb_erased = size_descendants(current_node->as_trie_node()); - - parent->set_child(current_node->child_of_char(), nullptr); - m_nb_elements -= nb_erased; - - if(parent->empty()) { - clear_empty_nodes(*parent); - } - - return nb_erased; - } - else { - const size_type nb_erased = m_nb_elements; - m_root.reset(nullptr); - m_nb_elements = 0; - - return nb_erased; - } - } - else { - const size_type nb_erased = current_node->as_hash_node().array_hash().size(); - - current_node->as_hash_node().array_hash().clear(); - m_nb_elements -= nb_erased; - - clear_empty_nodes(current_node->as_hash_node()); - - return nb_erased; - } - } - - void swap(htrie_hash& other) { - using std::swap; - - swap(m_hash, other.m_hash); - swap(m_root, other.m_root); - swap(m_nb_elements, other.m_nb_elements); - swap(m_max_load_factor, other.m_max_load_factor); - swap(m_burst_threshold, other.m_burst_threshold); - } - - /* - * Lookup - */ - template::value>::type* = nullptr> - U& at(const CharT* key, size_type key_size) { - return const_cast(static_cast(this)->at(key, key_size)); - } - - template::value>::type* = nullptr> - const U& at(const CharT* key, size_type key_size) const { - auto it_find = find(key, key_size); - if(it_find != cend()) { - return it_find.value(); - } - else { - THROW(std::out_of_range, "Couldn't find key."); - } - } - - //TODO optimize - template::value>::type* = nullptr> - U& access_operator(const CharT* key, size_type key_size) { - auto it_find = find(key, key_size); - if(it_find != cend()) { - return it_find.value(); - } - else { - return insert(key, key_size, U{}).first.value(); - } - } - - size_type count(const CharT* key, size_type key_size) const { - if(find(key, key_size) != cend()) { - return 1; - } - else { - return 0; - } - } - - iterator find(const CharT* key, size_type key_size) { - if(m_root == nullptr) { - return end(); - } - - return find_impl(*m_root, key, key_size); - } - - const_iterator find(const CharT* key, size_type key_size) const { - if(m_root == nullptr) { - return cend(); - } - - return find_impl(*m_root, key, key_size); - } - - std::pair equal_range(const CharT* key, size_type key_size) { - iterator it = find(key, key_size); - return std::make_pair(it, (it == end())?it:std::next(it)); - } - - std::pair equal_range(const CharT* key, size_type key_size) const { - const_iterator it = find(key, key_size); - return std::make_pair(it, (it == cend())?it:std::next(it)); - } - - std::pair equal_prefix_range(const CharT* prefix, size_type prefix_size) { - if(m_root == nullptr) { - return std::make_pair(prefix_end(), prefix_end()); - } - - return equal_prefix_range_impl(*m_root, prefix, prefix_size); - } - - std::pair equal_prefix_range(const CharT* prefix, - size_type prefix_size) const - { - if(m_root == nullptr) { - return std::make_pair(prefix_cend(), prefix_cend()); - } - - return equal_prefix_range_impl(*m_root, prefix, prefix_size); - } - - iterator longest_prefix(const CharT* key, size_type key_size) { - if(m_root == nullptr) { - return end(); - } - - return longest_prefix_impl(*m_root, key, key_size); - } - - const_iterator longest_prefix(const CharT* key, size_type key_size) const { - if(m_root == nullptr) { - return cend(); - } - - return longest_prefix_impl(*m_root, key, key_size); - } - - - /* - * Hash policy - */ - float max_load_factor() const { - return m_max_load_factor; - } - - void max_load_factor(float ml) { - m_max_load_factor = ml; - } - - /* - * Burst policy - */ - size_type burst_threshold() const { - return m_burst_threshold; - } - - void burst_threshold(size_type threshold) { - const size_type min_burst_threshold = MIN_BURST_THRESHOLD; - m_burst_threshold = std::max(min_burst_threshold, threshold); - } - - /* - * Observers - */ - hasher hash_function() const { - return m_hash; - } - - /* - * Other - */ - template - void serialize(Serializer& serializer) const { - serialize_impl(serializer); - } - - template - void deserialize(Deserializer& deserializer, bool hash_compatible) { - deserialize_impl(deserializer, hash_compatible); - } - -private: - /** - * Get the begin iterator by searching for the most left descendant node starting at search_start_node. - */ - template - Iterator cbegin(const anode& search_start_node) const noexcept { - if(search_start_node.is_hash_node()) { - return Iterator(search_start_node.as_hash_node()); - } - - const trie_node& tnode = search_start_node.as_trie_node().most_left_descendant_value_trie_node(); - if(tnode.val_node() != nullptr) { - return Iterator(tnode); - } - else { - const anode* first_child = tnode.first_child(); - tsl_ht_assert(first_child != nullptr); - - return Iterator(first_child->as_hash_node()); - } - } - - /** - * Get an iterator to the node that come just after the last descendant of search_start_node. - */ - template - Iterator cend(const anode& search_start_node) const noexcept { - if(search_start_node.parent() == nullptr) { - Iterator it; - it.set_as_end_iterator(); - - return it; - } - - const trie_node* current_trie_node = search_start_node.parent(); - const anode* next_node = current_trie_node->next_child(search_start_node); - - while(next_node == nullptr && current_trie_node->parent() != nullptr) { - const anode* current_child = current_trie_node; - current_trie_node = current_trie_node->parent(); - next_node = current_trie_node->next_child(*current_child); - } - - if(next_node == nullptr) { - Iterator it; - it.set_as_end_iterator(); - - return it; - } - else { - return cbegin(*next_node); - } - } - - prefix_iterator prefix_end() noexcept { - prefix_iterator it; - it.set_as_end_iterator(); - - return it; - } - - const_prefix_iterator prefix_cend() const noexcept { - const_prefix_iterator it; - it.set_as_end_iterator(); - - return it; - } - - size_type size_descendants(const anode& start_node) const { - auto first = cbegin(start_node); - auto last = cend(start_node); - - size_type nb_elements = 0; - while(first != last) { - if(first.m_read_trie_node_value) { - nb_elements++; - ++first; - } - else { - nb_elements += first.m_current_hash_node->array_hash().size(); - first.skip_hash_node(); - } - } - - return nb_elements; - } - - template - std::pair insert_impl(anode& search_start_node, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - anode* current_node = &search_start_node; - - for(size_type ikey = 0; ikey < key_size; ikey++) { - if(current_node->is_trie_node()) { - trie_node& tnode = current_node->as_trie_node(); - - if(tnode.child(key[ikey]) != nullptr) { - current_node = tnode.child(key[ikey]).get(); - } - else { - auto hnode = make_unique(m_hash, m_max_load_factor); - auto insert_it = hnode->array_hash().emplace_ks(key + ikey + 1, key_size - ikey - 1, - std::forward(value_args)...); - - tnode.set_child(key[ikey], std::move(hnode)); - m_nb_elements++; - - - return std::make_pair(iterator(tnode.child(key[ikey])->as_hash_node(), - insert_it.first), true); - } - } - else { - return insert_in_hash_node(current_node->as_hash_node(), - key + ikey, key_size - ikey, std::forward(value_args)...); - } - } - - - if(current_node->is_trie_node()) { - trie_node& tnode = current_node->as_trie_node(); - if(tnode.val_node() != nullptr) { - return std::make_pair(iterator(tnode), false); - } - else { - tnode.val_node() = make_unique(std::forward(value_args)...); - m_nb_elements++; - - return std::make_pair(iterator(tnode), true); - } - } - else { - return insert_in_hash_node(current_node->as_hash_node(), - "", 0, std::forward(value_args)...); - } - } - - template - std::pair insert_in_hash_node(hash_node& hnode, - const CharT* key, size_type key_size, ValueArgs&&... value_args) - { - if(need_burst(hnode)) { - std::unique_ptr new_node = burst(hnode); - if(hnode.parent() == nullptr) { - tsl_ht_assert(m_root.get() == &hnode); - - m_root = std::move(new_node); - return insert_impl(*m_root, key, key_size, std::forward(value_args)...); - } - else { - trie_node* parent = hnode.parent(); - const CharT child_of_char = hnode.child_of_char(); - - parent->set_child(child_of_char, std::move(new_node)); - - return insert_impl(*parent->child(child_of_char), - key, key_size, std::forward(value_args)...); - } - } - else { - auto it_insert = hnode.array_hash().emplace_ks(key, key_size, - std::forward(value_args)...); - if(it_insert.second) { - m_nb_elements++; - } - - return std::make_pair(iterator(hnode, it_insert.first), it_insert.second); - } - } - - - iterator erase(iterator pos) { - iterator next_pos = std::next(pos); - - if(pos.m_read_trie_node_value) { - tsl_ht_assert(pos.m_current_trie_node != nullptr && pos.m_current_trie_node->val_node() != nullptr); - - pos.m_current_trie_node->val_node().reset(nullptr); - m_nb_elements--; - - if(pos.m_current_trie_node->empty()) { - clear_empty_nodes(*pos.m_current_trie_node); - } - - return next_pos; - } - else { - tsl_ht_assert(pos.m_current_hash_node != nullptr); - auto next_array_hash_it = pos.m_current_hash_node->array_hash().erase(pos.m_array_hash_iterator); - m_nb_elements--; - - if(next_array_hash_it != pos.m_current_hash_node->array_hash().end()) { - // The erase on array_hash invalidated the next_pos iterator, return the right one. - return iterator(*pos.m_current_hash_node, next_array_hash_it); - } - else { - if(pos.m_current_hash_node->array_hash().empty()) { - clear_empty_nodes(*pos.m_current_hash_node); - } - - return next_pos; - } - } - } - - /** - * Clear all the empty nodes from the tree starting from empty_node (empty for a hash_node means that - * the array hash is empty, for a trie_node it means the node doesn't have any child or value_node - * associated to it). - */ - void clear_empty_nodes(anode& empty_node) noexcept { - tsl_ht_assert(!empty_node.is_trie_node() || - (empty_node.as_trie_node().empty() && empty_node.as_trie_node().val_node() == nullptr)); - tsl_ht_assert(!empty_node.is_hash_node() || empty_node.as_hash_node().array_hash().empty()); - - - trie_node* parent = empty_node.parent(); - if(parent == nullptr) { - tsl_ht_assert(m_root.get() == &empty_node); - tsl_ht_assert(m_nb_elements == 0); - m_root.reset(nullptr); - } - else if(parent->val_node() != nullptr || parent->nb_children() > 1) { - parent->child(empty_node.child_of_char()).reset(nullptr); - } - else if(parent->parent() == nullptr) { - tsl_ht_assert(m_root.get() == empty_node.parent()); - tsl_ht_assert(m_nb_elements == 0); - m_root.reset(nullptr); - } - else { - /** - * Parent is empty if we remove its empty_node child. - * Put empty_node as new child of the grand parent instead of parent (move hnode up, - * and delete the parent). And recurse. - * - * We can't just set grand_parent->child(parent->child_of_char()) to nullptr as - * the grand_parent may also become empty. We don't want empty trie_node with no value_node - * in the tree. - */ - trie_node* grand_parent = parent->parent(); - grand_parent->set_child(parent->child_of_char(), - std::move(parent->child(empty_node.child_of_char()))); - - - clear_empty_nodes(empty_node); - } - } - - - - - iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) { - return mutable_iterator(static_cast(this)->find_impl(search_start_node, key, key_size)); - } - - const_iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) const { - const anode* current_node = &search_start_node; - - for(size_type ikey = 0; ikey < key_size; ikey++) { - if(current_node->is_trie_node()) { - const trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(key[ikey]) == nullptr) { - return cend(); - } - else { - current_node = tnode->child(key[ikey]).get(); - } - } - else { - return find_in_hash_node(current_node->as_hash_node(), - key + ikey, key_size - ikey); - } - } - - - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - return (tnode.val_node() != nullptr)?const_iterator(tnode):cend(); - } - else { - return find_in_hash_node(current_node->as_hash_node(), "", 0); - } - } - - const_iterator find_in_hash_node(const hash_node& hnode, - const CharT* key, size_type key_size) const - { - auto it = hnode.array_hash().find_ks(key, key_size); - if(it != hnode.array_hash().end()) { - return const_iterator(hnode, it); - } - else { - return cend(); - } - } - - - iterator longest_prefix_impl(const anode& search_start_node, - const CharT* value, size_type value_size) - { - return mutable_iterator(static_cast(this)->longest_prefix_impl(search_start_node, - value, value_size)); - } - - const_iterator longest_prefix_impl(const anode& search_start_node, - const CharT* value, size_type value_size) const - { - const anode* current_node = &search_start_node; - const_iterator longest_found_prefix = cend(); - - for(size_type ivalue = 0; ivalue < value_size; ivalue++) { - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - - if(tnode.val_node() != nullptr) { - longest_found_prefix = const_iterator(tnode); - } - - if(tnode.child(value[ivalue]) == nullptr) { - return longest_found_prefix; - } - else { - current_node = tnode.child(value[ivalue]).get(); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - - /** - * Test the presence in the hash node of each substring from the - * remaining [ivalue, value_size) string starting from the longest. - * Also test the empty string. - */ - for(std::size_t i = ivalue; i <= value_size; i++) { - auto it = hnode.array_hash().find_ks(value + ivalue, (value_size - i)); - if(it != hnode.array_hash().end()) { - return const_iterator(hnode, it); - } - } - - return longest_found_prefix; - } - } - - if(current_node->is_trie_node()) { - const trie_node& tnode = current_node->as_trie_node(); - - if(tnode.val_node() != nullptr) { - longest_found_prefix = const_iterator(tnode); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - - auto it = hnode.array_hash().find_ks("", 0); - if(it != hnode.array_hash().end()) { - longest_found_prefix = const_iterator(hnode, it); - } - } - - return longest_found_prefix; - } - - - std::pair equal_prefix_range_impl( - anode& search_start_node, - const CharT* prefix, size_type prefix_size) - { - auto range = static_cast(this)->equal_prefix_range_impl(search_start_node, - prefix, prefix_size); - return std::make_pair(mutable_iterator(range.first), mutable_iterator(range.second)); - } - - std::pair equal_prefix_range_impl( - const anode& search_start_node, - const CharT* prefix, size_type prefix_size) const - { - const anode* current_node = &search_start_node; - - for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->is_trie_node()) { - const trie_node* tnode = ¤t_node->as_trie_node(); - - if(tnode->child(prefix[iprefix]) == nullptr) { - return std::make_pair(prefix_cend(), prefix_cend()); - } - else { - current_node = tnode->child(prefix[iprefix]).get(); - } - } - else { - const hash_node& hnode = current_node->as_hash_node(); - const_prefix_iterator begin(hnode.parent(), &hnode, - hnode.array_hash().begin(), hnode.array_hash().end(), - false, std::basic_string(prefix + iprefix, prefix_size - iprefix)); - begin.filter_prefix(); - - const_prefix_iterator end = cend(*current_node); - - return std::make_pair(begin, end); - } - } - - - const_prefix_iterator begin = cbegin(*current_node); - const_prefix_iterator end = cend(*current_node); - - return std::make_pair(begin, end); - } - - size_type erase_prefix_hash_node(hash_node& hnode, const CharT* prefix, size_type prefix_size) { - size_type nb_erased = 0; - - auto it = hnode.array_hash().begin(); - while(it != hnode.array_hash().end()) { - if(it.key_size() >= prefix_size && - std::memcmp(prefix, it.key(), prefix_size * sizeof(CharT)) == 0) - { - it = hnode.array_hash().erase(it); - ++nb_erased; - --m_nb_elements; - } - else { - ++it; - } - } - - return nb_erased; - } - - - /* - * Burst - */ - bool need_burst(hash_node& node) const { - return node.array_hash().size() >= m_burst_threshold; - } - - - /** - * Burst the node and use the copy constructor instead of move constructor for the values. - * Also use this method for trivial value types like int, int*, ... as it requires - * less book-keeping (thus faster) than the burst using move constructors. - */ - template::value && - std::is_copy_constructible::value && - (!std::is_nothrow_move_constructible::value || - !std::is_nothrow_move_assignable::value || - std::is_arithmetic::value || - std::is_pointer::value)>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - const std::array first_char_count = - get_first_char_count(node.array_hash().cbegin(), - node.array_hash().cend()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(it.value()); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, it.value()); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - /** - * Burst the node and use the move constructor and move assign operator - */ - template::value && - std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value && - !std::is_arithmetic::value && - !std::is_pointer::value>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - /** - * We burst the node->array_hash() into multiple arrays hash. While doing so, we move each value in - * the node->array_hash() into the new arrays hash. After each move, we save a pointer to where the value - * has been moved. In case of exception, we rollback these values into the original node->array_hash(). - */ - std::vector moved_values_rollback; - moved_values_rollback.reserve(node.array_hash().size()); - - const std::array first_char_count = - get_first_char_count(node.array_hash().cbegin(), node.array_hash().cend()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().begin(); it != node.array_hash().end(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(std::move(it.value())); - moved_values_rollback.push_back(std::addressof(new_node->val_node()->m_value)); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - auto it_insert = hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, - std::move(it.value())); - moved_values_rollback.push_back(std::addressof(it_insert.first.value())); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - template::value>::type* = nullptr> - std::unique_ptr burst(hash_node& node) { - const std::array first_char_count = - get_first_char_count(node.array_hash().begin(), node.array_hash().end()); - - - auto new_node = make_unique(); - for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { - if(it.key_size() == 0) { - new_node->val_node() = make_unique(); - } - else { - hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); - hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1); - } - } - - - tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); - return new_node; - } - - std::array get_first_char_count(typename array_hash_type::const_iterator begin, - typename array_hash_type::const_iterator end) const - { - std::array count{{}}; - for(auto it = begin; it != end; ++it) { - if(it.key_size() == 0) { - continue; - } - - count[as_position(it.key()[0])]++; - } - - return count; - } - - - hash_node& get_hash_node_for_char(const std::array& first_char_count, - trie_node& tnode, CharT for_char) - { - if(tnode.child(for_char) == nullptr) { - const size_type nb_buckets = - size_type( - std::ceil(float(first_char_count[as_position(for_char)] + - HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT/2) - / m_max_load_factor - )); - - tnode.set_child(for_char, - make_unique(nb_buckets, m_hash, m_max_load_factor)); - } - - return tnode.child(for_char)->as_hash_node(); - } - - iterator mutable_iterator(const_iterator it) noexcept { - // end iterator or reading from a trie node value - if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { - typename array_hash_type::iterator default_it; - - return iterator(const_cast(it.m_current_trie_node), nullptr, - default_it, default_it, it.m_read_trie_node_value); - } - else { - hash_node* hnode = const_cast(it.m_current_hash_node); - return iterator(const_cast(it.m_current_trie_node), hnode, - hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), - hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), - it.m_read_trie_node_value); - } - } - - prefix_iterator mutable_iterator(const_prefix_iterator it) noexcept { - // end iterator or reading from a trie node value - if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { - typename array_hash_type::iterator default_it; - - return prefix_iterator(const_cast(it.m_current_trie_node), nullptr, - default_it, default_it, it.m_read_trie_node_value, ""); - } - else { - hash_node* hnode = const_cast(it.m_current_hash_node); - return prefix_iterator(const_cast(it.m_current_trie_node), hnode, - hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), - hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), - it.m_read_trie_node_value, it.m_prefix_filter); - } - } - - template - void serialize_impl(Serializer& serializer) const { - const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; - serializer(version); - - const slz_size_type nb_elements = m_nb_elements; - serializer(nb_elements); - - const float max_load_factor = m_max_load_factor; - serializer(max_load_factor); - - const slz_size_type burst_threshold = m_burst_threshold; - serializer(burst_threshold); - - - std::basic_string str_buffer; - - auto it = begin(); - auto last = end(); - - while(it != last) { - // Serialize trie node value - if(it.m_read_trie_node_value) { - const CharT node_type = static_cast::type>(slz_node_type::TRIE_NODE); - serializer(&node_type, 1); - - it.key(str_buffer); - - const slz_size_type str_size = str_buffer.size(); - serializer(str_size); - serializer(str_buffer.data(), str_buffer.size()); - serialize_value(serializer, it); - - - ++it; - } - // Serialize hash node values - else { - const CharT node_type = static_cast::type>(slz_node_type::HASH_NODE); - serializer(&node_type, 1); - - it.hash_node_prefix(str_buffer); - - const slz_size_type str_size = str_buffer.size(); - serializer(str_size); - serializer(str_buffer.data(), str_buffer.size()); - - const hash_node* hnode = it.m_current_hash_node; - tsl_ht_assert(hnode != nullptr); - hnode->array_hash().serialize(serializer); - - - it.skip_hash_node(); - } - } - } - - template::value>::type* = nullptr> - void serialize_value(Serializer& /*serializer*/, const_iterator /*it*/) const { - } - - template::value>::type* = nullptr> - void serialize_value(Serializer& serializer, const_iterator it) const { - serializer(it.value()); - } - - template - void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { - tsl_ht_assert(m_nb_elements == 0 && m_root == nullptr); // Current trie must be empty - - const slz_size_type version = deserialize_value(deserializer); - // For now we only have one version of the serialization protocol. - // If it doesn't match there is a problem with the file. - if(version != SERIALIZATION_PROTOCOL_VERSION) { - THROW(std::runtime_error, "Can't deserialize the htrie_map/set. The protocol version header is invalid."); - } - - - const slz_size_type nb_elements = deserialize_value(deserializer); - const float max_load_factor = deserialize_value(deserializer); - const slz_size_type burst_threshold = deserialize_value(deserializer); - - this->burst_threshold(numeric_cast(burst_threshold, "Deserialized burst_threshold is too big.")); - this->max_load_factor(max_load_factor); - - - std::vector str_buffer; - while(m_nb_elements < nb_elements) { - CharT node_type_marker; - deserializer(&node_type_marker, 1); - - static_assert(std::is_same::type>::value, ""); - const slz_node_type node_type = static_cast(node_type_marker); - if(node_type == slz_node_type::TRIE_NODE) { - const std::size_t str_size = numeric_cast(deserialize_value(deserializer), - "Deserialized str_size is too big."); - - str_buffer.resize(str_size); - deserializer(str_buffer.data(), str_size); - - - trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size); - deserialize_value_node(deserializer, current_node); - m_nb_elements++; - } - else if(node_type == slz_node_type::HASH_NODE) { - const std::size_t str_size = numeric_cast(deserialize_value(deserializer), - "Deserialized str_size is too big."); - - if(str_size == 0) { - tsl_ht_assert(m_nb_elements == 0 && !m_root); - - m_root = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); - m_nb_elements += m_root->as_hash_node().array_hash().size(); - - tsl_ht_assert(m_nb_elements == nb_elements); - } - else { - str_buffer.resize(str_size); - deserializer(str_buffer.data(), str_size); - - - auto hnode = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); - m_nb_elements += hnode->array_hash().size(); - - trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size - 1); - current_node->set_child(str_buffer[str_size - 1], std::move(hnode)); - } - } - else { - THROW(std::runtime_error, "Unknown deserialized node type."); - } - } - - tsl_ht_assert(m_nb_elements == nb_elements); - } - - trie_node* insert_prefix_trie_nodes(const CharT* prefix, std::size_t prefix_size) { - if(m_root == nullptr) { - m_root = make_unique(); - } - - trie_node* current_node = &m_root->as_trie_node(); - for(std::size_t iprefix = 0; iprefix < prefix_size; iprefix++) { - if(current_node->child(prefix[iprefix]) == nullptr) { - current_node->set_child(prefix[iprefix], make_unique()); - } - - current_node = ¤t_node->child(prefix[iprefix])->as_trie_node(); - } - - return current_node; - } - - template::value>::type* = nullptr> - void deserialize_value_node(Deserializer& /*deserializer*/, trie_node* current_node) { - tsl_ht_assert(!current_node->val_node()); - current_node->val_node() = make_unique(); - } - - template::value>::type* = nullptr> - void deserialize_value_node(Deserializer& deserializer, trie_node* current_node) { - tsl_ht_assert(!current_node->val_node()); - current_node->val_node() = make_unique(deserialize_value(deserializer)); - } - - template - static U deserialize_value(Deserializer& deserializer) { - // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword - #if defined (_MSC_VER) && _MSC_VER < 1910 - return deserializer.Deserializer::operator()(); - #else - return deserializer.Deserializer::template operator()(); - #endif - } - - // Same as std::make_unique for non-array types which is only available in C++14 (we need to support C++11). - template - static std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new U(std::forward(args)...)); - } - -public: - static constexpr float HASH_NODE_DEFAULT_MAX_LOAD_FACTOR = 8.0f; - static const size_type DEFAULT_BURST_THRESHOLD = 16384; - -private: - - /** - * Fixed size type used to represent size_type values on serialization. Need to be big enough - * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. - */ - using slz_size_type = std::uint64_t; - enum class slz_node_type: CharT { TRIE_NODE = 0, HASH_NODE = 1 }; - - /** - * Protocol version currenlty used for serialization. - */ - static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; - - static const size_type HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT = 32; - static const size_type MIN_BURST_THRESHOLD = 4; - - std::unique_ptr m_root; - size_type m_nb_elements; - Hash m_hash; - float m_max_load_factor; - size_type m_burst_threshold; - -}; - -} // end namespace detail_htrie_hash -} // end namespace tsl - -#endif diff --git a/ios/include/tsl/htrie_map.h b/ios/include/tsl/htrie_map.h deleted file mode 100644 index 59712c5e..00000000 --- a/ios/include/tsl/htrie_map.h +++ /dev/null @@ -1,647 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_MAP_H -#define TSL_HTRIE_MAP_H - -#include -#include -#include -#include -#include -#include "htrie_hash.h" - -namespace tsl { - -/** - * Implementation of a hat-trie map. - * - * The value T must be either nothrow move-constructible/assignable, copy-constructible or both. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. - * See max_key_size() for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert, emplace, operator[]: always invalidate the iterators. - * - erase: always invalidate the iterators. - */ -template, - class KeySizeT = std::uint16_t> -class htrie_map { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_htrie_hash::htrie_hash; - -public: - using char_type = typename ht::char_type; - using mapped_type = T; - using key_size_type = typename ht::key_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - using prefix_iterator = typename ht::prefix_iterator; - using const_prefix_iterator = typename ht::const_prefix_iterator; - -public: - explicit htrie_map(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - ht::DEFAULT_BURST_THRESHOLD) - { - } - - explicit htrie_map(size_type burst_threshold, - const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - burst_threshold) - { - } - - template::value>::type* = nullptr> - htrie_map(InputIt first, InputIt last, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(first, last); - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_map(std::initializer_list, T>> init, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(init); - } -#else - htrie_map(std::initializer_list> init, - const Hash& hash = Hash()): htrie_map(hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_map& operator=(std::initializer_list, T>> ilist) { - clear(); - insert(ilist); - - return *this; - } -#else - htrie_map& operator=(std::initializer_list> ilist) { - clear(); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - - /** - * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. - */ - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - - std::pair insert_ks(const CharT* key, size_type key_size, const T& value) { - return m_ht.insert(key, key_size, value); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, const T& value) { - return m_ht.insert(key.data(), key.size(), value); - } -#else - std::pair insert(const CharT* key, const T& value) { - return m_ht.insert(key, std::strlen(key), value); - } - - std::pair insert(const std::basic_string& key, const T& value) { - return m_ht.insert(key.data(), key.size(), value); - } -#endif - - - - std::pair insert_ks(const CharT* key, size_type key_size, T&& value) { - return m_ht.insert(key, key_size, std::move(value)); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key, T&& value) { - return m_ht.insert(key.data(), key.size(), std::move(value)); - } -#else - std::pair insert(const CharT* key, T&& value) { - return m_ht.insert(key, std::strlen(key), std::move(value)); - } - - std::pair insert(const std::basic_string& key, T&& value) { - return m_ht.insert(key.data(), key.size(), std::move(value)); - } -#endif - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - for(auto it = first; it != last; ++it) { - insert_pair(*it); - } - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - void insert(std::initializer_list, T>> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - - template - std::pair emplace_ks(const CharT* key, size_type key_size, Args&&... args) { - return m_ht.insert(key, key_size, std::forward(args)...); - } -#ifdef TSL_HT_HAS_STRING_VIEW - template - std::pair emplace(const std::basic_string_view& key, Args&&... args) { - return m_ht.insert(key.data(), key.size(), std::forward(args)...); - } -#else - template - std::pair emplace(const CharT* key, Args&&... args) { - return m_ht.insert(key, std::strlen(key), std::forward(args)...); - } - - template - std::pair emplace(const std::basic_string& key, Args&&... args) { - return m_ht.insert(key.data(), key.size(), std::forward(args)...); - } -#endif - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::strlen(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - - - - /** - * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. - */ - size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.erase_prefix(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string_view& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const CharT* prefix) { - return m_ht.erase_prefix(prefix, std::strlen(prefix)); - } - - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#endif - - - - void swap(htrie_map& other) { other.m_ht.swap(m_ht); } - - /* - * Lookup - */ - T& at_ks(const CharT* key, size_type key_size) { return m_ht.at(key, key_size); } - const T& at_ks(const CharT* key, size_type key_size) const { return m_ht.at(key, key_size); } - -#ifdef TSL_HT_HAS_STRING_VIEW - T& at(const std::basic_string_view& key) { return m_ht.at(key.data(), key.size()); } - const T& at(const std::basic_string_view& key) const { return m_ht.at(key.data(), key.size()); } -#else - T& at(const CharT* key) { return m_ht.at(key, std::strlen(key)); } - const T& at(const CharT* key) const { return m_ht.at(key, std::strlen(key)); } - - T& at(const std::basic_string& key) { return m_ht.at(key.data(), key.size()); } - const T& at(const std::basic_string& key) const { return m_ht.at(key.data(), key.size()); } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - T& operator[](const std::basic_string_view& key) { return m_ht.access_operator(key.data(), key.size()); } -#else - T& operator[](const CharT* key) { return m_ht.access_operator(key, std::strlen(key)); } - T& operator[](const std::basic_string& key) { return m_ht.access_operator(key.data(), key.size()); } -#endif - - - - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - - - - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::strlen(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::strlen(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - - - - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - - - /** - * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair - * of iterator, the first being the begin iterator and the second being the end iterator. - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.equal_prefix_range(prefix, prefix_size); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { - return m_ht.equal_prefix_range(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) const { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#endif - - - - /** - * Return the element in the trie which is the longest prefix of `key`. If no - * element in the trie is a prefix of `key`, the end iterator is returned. - * - * Example: - * - * tsl::htrie_map map = {{"/foo", 1}, {"/foo/bar", 1}}; - * - * map.longest_prefix("/foo"); // returns {"/foo", 1} - * map.longest_prefix("/foo/baz"); // returns {"/foo", 1} - * map.longest_prefix("/foo/bar/baz"); // returns {"/foo/bar", 1} - * map.longest_prefix("/foo/bar/"); // returns {"/foo/bar", 1} - * map.longest_prefix("/bar"); // returns end() - * map.longest_prefix(""); // returns end() - */ - iterator longest_prefix_ks(const CharT* key, size_type key_size) { - return m_ht.longest_prefix(key, key_size); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { - return m_ht.longest_prefix(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string_view& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string_view& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#else - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const CharT* key) { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const CharT* key) const { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#endif - - - - /* - * Hash policy - */ - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - - /* - * Burst policy - */ - size_type burst_threshold() const { return m_ht.burst_threshold(); } - void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - - - - /* - * Other - */ - - /** - * Serialize the map through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - - /** - * Deserialize a previously serialized map through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash map part of the hat-trie is hash compatible with the serialized map, the deserialization process - * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * and KeySizeT must behave the same than the ones used in the serialized map. Otherwise the behaviour is undefined - * with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` and `T` of the `htrie_map` are not the same as the - * types used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static htrie_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { - htrie_map map; - map.m_ht.deserialize(deserializer, hash_compatible); - - return map; - } - - friend bool operator==(const htrie_map& lhs, const htrie_map& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - std::string key_buffer; - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - it.key(key_buffer); - - const auto it_element_rhs = rhs.find(key_buffer); - if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const htrie_map& lhs, const htrie_map& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(htrie_map& lhs, htrie_map& rhs) { - lhs.swap(rhs); - } - -private: - template - void insert_pair(const std::pair& value) { - insert(value.first, value.second); - } - - template - void insert_pair(std::pair&& value) { - insert(value.first, std::move(value.second)); - } - -private: - ht m_ht; -}; - -} // end namespace tsl - -#endif diff --git a/ios/include/tsl/htrie_set.h b/ios/include/tsl/htrie_set.h deleted file mode 100644 index e2f40adc..00000000 --- a/ios/include/tsl/htrie_set.h +++ /dev/null @@ -1,586 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2017 Thibaut Goetghebuer-Planchon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TSL_HTRIE_SET_H -#define TSL_HTRIE_SET_H - -#include -#include -#include -#include -#include -#include "htrie_hash.h" - -namespace tsl { - -/** - * Implementation of a hat-trie set. - * - * The size of a key string is limited to std::numeric_limits::max() - 1. - * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. - * See max_key_size() for an easy access to this limit. - * - * Iterators invalidation: - * - clear, operator=: always invalidate the iterators. - * - insert: always invalidate the iterators. - * - erase: always invalidate the iterators. - */ -template, - class KeySizeT = std::uint16_t> -class htrie_set { -private: - template - using is_iterator = tsl::detail_array_hash::is_iterator; - - using ht = tsl::detail_htrie_hash::htrie_hash; - -public: - using char_type = typename ht::char_type; - using key_size_type = typename ht::key_size_type; - using size_type = typename ht::size_type; - using hasher = typename ht::hasher; - using iterator = typename ht::iterator; - using const_iterator = typename ht::const_iterator; - using prefix_iterator = typename ht::prefix_iterator; - using const_prefix_iterator = typename ht::const_prefix_iterator; - -public: - explicit htrie_set(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - ht::DEFAULT_BURST_THRESHOLD) - { - } - - explicit htrie_set(size_type burst_threshold, - const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, - burst_threshold) - { - } - - template::value>::type* = nullptr> - htrie_set(InputIt first, InputIt last, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(first, last); - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_set(std::initializer_list> init, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(init); - } -#else - htrie_set(std::initializer_list init, - const Hash& hash = Hash()): htrie_set(hash) - { - insert(init); - } -#endif - - - -#ifdef TSL_HT_HAS_STRING_VIEW - htrie_set& operator=(std::initializer_list> ilist) { - clear(); - insert(ilist); - - return *this; - } -#else - htrie_set& operator=(std::initializer_list ilist) { - clear(); - insert(ilist); - - return *this; - } -#endif - - - - /* - * Iterators - */ - iterator begin() noexcept { return m_ht.begin(); } - const_iterator begin() const noexcept { return m_ht.begin(); } - const_iterator cbegin() const noexcept { return m_ht.cbegin(); } - - iterator end() noexcept { return m_ht.end(); } - const_iterator end() const noexcept { return m_ht.end(); } - const_iterator cend() const noexcept { return m_ht.cend(); } - - - /* - * Capacity - */ - bool empty() const noexcept { return m_ht.empty(); } - size_type size() const noexcept { return m_ht.size(); } - size_type max_size() const noexcept { return m_ht.max_size(); } - size_type max_key_size() const noexcept { return m_ht.max_key_size(); } - - /** - * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. - */ - void shrink_to_fit() { m_ht.shrink_to_fit(); } - - - /* - * Modifiers - */ - void clear() noexcept { m_ht.clear(); } - - - - std::pair insert_ks(const CharT* key, size_type key_size) { - return m_ht.insert(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair insert(const std::basic_string_view& key) { - return m_ht.insert(key.data(), key.size()); - } -#else - std::pair insert(const CharT* key) { - return m_ht.insert(key, std::strlen(key)); - } - - std::pair insert(const std::basic_string& key) { - return m_ht.insert(key.data(), key.size()); - } -#endif - - - - template::value>::type* = nullptr> - void insert(InputIt first, InputIt last) { - for(auto it = first; it != last; ++it) { - insert(*it); - } - } - - - -#ifdef TSL_HT_HAS_STRING_VIEW - void insert(std::initializer_list> ilist) { - insert(ilist.begin(), ilist.end()); - } -#else - void insert(std::initializer_list ilist) { - insert(ilist.begin(), ilist.end()); - } -#endif - - - - std::pair emplace_ks(const CharT* key, size_type key_size) { - return m_ht.insert(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair emplace(const std::basic_string_view& key) { - return m_ht.insert(key.data(), key.size()); - } -#else - std::pair emplace(const CharT* key) { - return m_ht.insert(key, std::strlen(key)); - } - - std::pair emplace(const std::basic_string& key) { - return m_ht.insert(key.data(), key.size()); - } -#endif - - - - iterator erase(const_iterator pos) { return m_ht.erase(pos); } - iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } - - - - size_type erase_ks(const CharT* key, size_type key_size) { - return m_ht.erase(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type erase(const std::basic_string_view& key) { - return m_ht.erase(key.data(), key.size()); - } -#else - size_type erase(const CharT* key) { - return m_ht.erase(key, std::strlen(key)); - } - - size_type erase(const std::basic_string& key) { - return m_ht.erase(key.data(), key.size()); - } -#endif - - - - /** - * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. - */ - size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.erase_prefix(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string_view& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const CharT* prefix) { - return m_ht.erase_prefix(prefix, std::strlen(prefix)); - } - - /** - * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) - */ - size_type erase_prefix(const std::basic_string& prefix) { - return m_ht.erase_prefix(prefix.data(), prefix.size()); - } -#endif - - - - void swap(htrie_set& other) { other.m_ht.swap(m_ht); } - - - /* - * Lookup - */ - size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } -#ifdef TSL_HT_HAS_STRING_VIEW - size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } -#else - size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } - size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } -#endif - - - - iterator find_ks(const CharT* key, size_type key_size) { - return m_ht.find(key, key_size); - } - - const_iterator find_ks(const CharT* key, size_type key_size) const { - return m_ht.find(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - iterator find(const std::basic_string_view& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string_view& key) const { - return m_ht.find(key.data(), key.size()); - } -#else - iterator find(const CharT* key) { - return m_ht.find(key, std::strlen(key)); - } - - const_iterator find(const CharT* key) const { - return m_ht.find(key, std::strlen(key)); - } - - iterator find(const std::basic_string& key) { - return m_ht.find(key.data(), key.size()); - } - - const_iterator find(const std::basic_string& key) const { - return m_ht.find(key.data(), key.size()); - } -#endif - - - - std::pair equal_range_ks(const CharT* key, size_type key_size) { - return m_ht.equal_range(key, key_size); - } - - std::pair equal_range_ks(const CharT* key, size_type key_size) const { - return m_ht.equal_range(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - std::pair equal_range(const std::basic_string_view& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string_view& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#else - std::pair equal_range(const CharT* key) { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const CharT* key) const { - return m_ht.equal_range(key, std::strlen(key)); - } - - std::pair equal_range(const std::basic_string& key) { - return m_ht.equal_range(key.data(), key.size()); - } - - std::pair equal_range(const std::basic_string& key) const { - return m_ht.equal_range(key.data(), key.size()); - } -#endif - - - - /** - * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair - * of iterator, the first being the begin iterator and the second being the end iterator. - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { - return m_ht.equal_prefix_range(prefix, prefix_size); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { - return m_ht.equal_prefix_range(prefix, prefix_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string_view& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#else - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const CharT* prefix) const { - return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } - - /** - * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) - */ - std::pair equal_prefix_range(const std::basic_string& prefix) const { - return m_ht.equal_prefix_range(prefix.data(), prefix.size()); - } -#endif - - - - /** - * Return the element in the trie which is the longest prefix of `key`. If no - * element in the trie is a prefix of `key`, the end iterator is returned. - * - * Example: - * - * tsl::htrie_set set = {"/foo", "/foo/bar"}; - * - * set.longest_prefix("/foo"); // returns "/foo" - * set.longest_prefix("/foo/baz"); // returns "/foo" - * set.longest_prefix("/foo/bar/baz"); // returns "/foo/bar" - * set.longest_prefix("/foo/bar/"); // returns "/foo/bar" - * set.longest_prefix("/bar"); // returns end() - * set.longest_prefix(""); // returns end() - */ - iterator longest_prefix_ks(const CharT* key, size_type key_size) { - return m_ht.longest_prefix(key, key_size); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { - return m_ht.longest_prefix(key, key_size); - } -#ifdef TSL_HT_HAS_STRING_VIEW - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string_view& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string_view& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#else - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const CharT* key) { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const CharT* key) const { - return m_ht.longest_prefix(key, std::strlen(key)); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - iterator longest_prefix(const std::basic_string& key) { - return m_ht.longest_prefix(key.data(), key.size()); - } - - /** - * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) - */ - const_iterator longest_prefix(const std::basic_string& key) const { - return m_ht.longest_prefix(key.data(), key.size()); - } -#endif - - - - /* - * Hash policy - */ - float max_load_factor() const { return m_ht.max_load_factor(); } - void max_load_factor(float ml) { m_ht.max_load_factor(ml); } - - - /* - * Burst policy - */ - size_type burst_threshold() const { return m_ht.burst_threshold(); } - void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } - - - /* - * Observers - */ - hasher hash_function() const { return m_ht.hash_function(); } - - - - /* - * Other - */ - - /** - * Serialize the set through the `serializer` parameter. - * - * The `serializer` parameter must be a function object that supports the following calls: - * - `void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(const CharT* value, std::size_t value_size);` - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes - * in the hands of the `Serializer` function object if compatibility is required. - */ - template - void serialize(Serializer& serializer) const { - m_ht.serialize(serializer); - } - - - /** - * Deserialize a previously serialized set through the `deserializer` parameter. - * - * The `deserializer` parameter must be a function object that supports the following calls: - * - `template U operator()();` where the types `std::uint64_t` and `float` must be supported for U. - * - `void operator()(CharT* value_out, std::size_t value_size);` - * - * If the deserialized hash set part of the hat-trie is hash compatible with the serialized set, the deserialization process - * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), - * and KeySizeT must behave the same than the ones used in the serialized set. Otherwise the behaviour is undefined - * with `hash_compatible` sets to true. - * - * The behaviour is undefined if the type `CharT` of the `htrie_set` is not the same as the - * type used during serialization. - * - * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it - * deserializes in the hands of the `Deserializer` function object if compatibility is required. - */ - template - static htrie_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { - htrie_set set; - set.m_ht.deserialize(deserializer, hash_compatible); - - return set; - } - - friend bool operator==(const htrie_set& lhs, const htrie_set& rhs) { - if(lhs.size() != rhs.size()) { - return false; - } - - std::string key_buffer; - for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { - it.key(key_buffer); - - const auto it_element_rhs = rhs.find(key_buffer); - if(it_element_rhs == rhs.cend()) { - return false; - } - } - - return true; - } - - friend bool operator!=(const htrie_set& lhs, const htrie_set& rhs) { - return !operator==(lhs, rhs); - } - - friend void swap(htrie_set& lhs, htrie_set& rhs) { - lhs.swap(rhs); - } - -private: - ht m_ht; -}; - -} // end namespace tsl - -#endif diff --git a/ios/include/utils/BinaryTreeArray.h b/ios/include/utils/BinaryTreeArray.h deleted file mode 100644 index d147d069..00000000 --- a/ios/include/utils/BinaryTreeArray.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_BINARYTREEARRAY_H -#define TNT_UTILS_BINARYTREEARRAY_H - -#include - -#include - -#include -#include -#include - -namespace utils { - -class BinaryTreeArray { - - // Simple fixed capacity stack - template::value>::type> - class stack { - TYPE mElements[CAPACITY]; - size_t mSize = 0; - public: - bool empty() const noexcept { return mSize == 0; } - void push(TYPE const& v) noexcept { - assert(mSize < CAPACITY); - mElements[mSize++] = v; - } - void pop() noexcept { - assert(mSize > 0); - --mSize; - } - const TYPE& back() const noexcept { - return mElements[mSize - 1]; - } - }; - -public: - static size_t count(size_t height) noexcept { return (1u << height) - 1; } - static size_t left(size_t i, size_t height) noexcept { return i + 1; } - static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); } - - // this builds the depth-first binary tree array top down (post-order) - template - static void traverse(size_t height, Leaf leaf, Node node) noexcept { - - struct TNode { - uint32_t index; - uint32_t col; - uint32_t height; - uint32_t next; - - bool isLeaf() const noexcept { return height == 1; } - size_t left() const noexcept { return BinaryTreeArray::left(index, height); } - size_t right() const noexcept { return BinaryTreeArray::right(index, height); } - }; - - stack stack; - stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) }); - - uint32_t prevLeft = 0; - uint32_t prevRight = 0; - uint32_t prevIndex = 0; - while (!stack.empty()) { - TNode const* const UTILS_RESTRICT curr = &stack.back(); - const bool isLeaf = curr->isLeaf(); - const uint32_t index = curr->index; - const uint32_t l = (uint32_t)curr->left(); - const uint32_t r = (uint32_t)curr->right(); - - if (prevLeft == index || prevRight == index) { - if (!isLeaf) { - // the 'next' node of our left node's right descendants is our right child - stack.push({ l, 2 * curr->col, curr->height - 1, r }); - } - } else if (l == prevIndex) { - if (!isLeaf) { - // the 'next' node of our right child is our own 'next' sibling - stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next }); - } - } else { - if (!isLeaf) { - node(index, l, r, curr->next); - } else { - leaf(index, curr->col, curr->next); - } - stack.pop(); - } - - prevLeft = l; - prevRight = r; - prevIndex = index; - } - } -}; - -} // namespace utils - -#endif //TNT_UTILS_BINARYTREEARRAY_H diff --git a/ios/include/utils/CString.h b/ios/include/utils/CString.h index 18e57307..da809d11 100644 --- a/ios/include/utils/CString.h +++ b/ios/include/utils/CString.h @@ -86,7 +86,9 @@ public: constexpr StaticString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) : mString(other), mLength(size_type(N - 1)), - mHash(computeHash(other)) { + mHash(computeHash(other, N - 1)) { + // we rely on inlining for computeHash. It would be nice to do this with constexpr + // instead, but unfortunately 'other' is not constexpr once a parameter. } // assignment from a string literal @@ -94,7 +96,9 @@ public: StaticString& operator=(StringLiteral const& other) noexcept { mString = other; mLength = size_type(N - 1); - mHash = computeHash(other); + // we rely on inlining for computeHash. It would be nice to do this with constexpr + // instead, but unfortunately 'other' is not constexpr once a parameter. + mHash = computeHash(other, N - 1); return *this; } @@ -103,11 +107,7 @@ public: StaticString r; r.mString = literal; r.mLength = size_type(length); - size_type hash = 5381; - while (int c = *literal++) { - hash = (hash * 33u) ^ size_type(c); - } - r.mHash = hash; + r.mHash = computeHash(literal, length); return r; } @@ -154,9 +154,9 @@ private: size_type mLength = 0; size_type mHash = 0; - template - static constexpr size_type computeHash(StringLiteral const& s) noexcept { + static constexpr size_type computeHash(const char* s, const size_t N) noexcept { size_type hash = 5381; + UTILS_NOUNROLL for (size_t i = 0; i < N - 1; i++) { hash = (hash * 33u) ^ size_type(s[i]); } @@ -206,12 +206,17 @@ public: // inside the string (i.e. it can contain nulls or non-ASCII encodings). CString(const char* cstr, size_t length); + // Allocates memory for a string of size length plus space for the null terminating character. + // Also initializes the memory to 0. This constructor can be used to hold arbitrary data + // inside the string. + explicit CString(size_t length); + // Allocates memory and copies traditional C string content. Unlike the above constructor, this // does not alllow embedded nulls. This is explicit because this operation is costly. explicit CString(const char* cstr); template - explicit CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) + CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) : CString(other, N - 1) { } diff --git a/ios/include/utils/CallStack.h b/ios/include/utils/CallStack.h index 930c3522..291a748c 100644 --- a/ios/include/utils/CallStack.h +++ b/ios/include/utils/CallStack.h @@ -69,7 +69,7 @@ public: /** Demangles a C++ type name */ static utils::CString demangleTypeName(const char* mangled); - template + template static utils::CString typeName() { #if UTILS_HAS_RTTI return demangleTypeName(typeid(T).name()); diff --git a/ios/include/utils/Condition.h b/ios/include/utils/Condition.h deleted file mode 100644 index 2ed2c07f..00000000 --- a/ios/include/utils/Condition.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_CONDITION_H -#define TNT_UTILS_CONDITION_H - -#if defined(__linux__) -#include -#else -#include -#endif - -#endif // TNT_UTILS_CONDITION_H diff --git a/ios/include/utils/CountDownLatch.h b/ios/include/utils/CountDownLatch.h deleted file mode 100644 index 6367fffc..00000000 --- a/ios/include/utils/CountDownLatch.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_COUNTDOWNLATCH_H -#define TNT_UTILS_COUNTDOWNLATCH_H - -#include - -// note: we use our version of mutex/condition to keep this public header STL free -#include -#include - -namespace utils { - -/** - * A count down latch is used to block one or several threads until the latch is signaled - * a certain number of times. - * - * Threads entering the latch are blocked until the latch is signaled enough times. - * - * @see CyclicBarrier - */ -class CountDownLatch { -public: - /** - * Creates a count down latch with a specified count. The minimum useful value is 1. - * @param count the latch counter initial value - */ - explicit CountDownLatch(size_t count) noexcept; - ~CountDownLatch() = default; - - /** - * Blocks until latch() is called \p count times. - * @see CountDownLatch(size_t count) - */ - void await() noexcept; - - /** - * Releases threads blocked in await() when called \p count times. Calling latch() more than - * \p count times has no effect. - * @see reset() - */ - void latch() noexcept; - - /** - * Resets the count-down latch to the given value. - * - * @param new_count New latch count. A value of zero will immediately unblock all waiting - * threads. - * - * @warning Use with caution. It's only safe to reset the latch count when you're sure - * that no threads are waiting in await(). This can be guaranteed in various ways, for - * instance, if you have a single thread calling await(), you could call reset() from that - * thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch - * are at a known place (i.e.: not in await()) when reset() is called. - */ - void reset(size_t new_count) noexcept; - - /** - * @return the number of times latch() has been called since construction or reset. - * @see reset(), CountDownLatch(size_t count) - */ - size_t getCount() const noexcept; - - CountDownLatch() = delete; - CountDownLatch(const CountDownLatch&) = delete; - CountDownLatch& operator=(const CountDownLatch&) = delete; - -private: - uint32_t m_initial_count; - uint32_t m_remaining_count; - mutable Mutex m_lock; - mutable Condition m_cv; -}; - -} // namespace utils - -#endif // TNT_UTILS_COUNTDOWNLATCH_H diff --git a/ios/include/utils/CyclicBarrier.h b/ios/include/utils/CyclicBarrier.h deleted file mode 100644 index dae118e8..00000000 --- a/ios/include/utils/CyclicBarrier.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_CYCLIC_BARRIER_H -#define TNT_UTILS_CYCLIC_BARRIER_H - -#include - -// note: we use our version of mutex/condition to keep this public header STL free -#include -#include - -namespace utils { - -/** - * A cyclic barrier is used to synchronize several threads to a particular execution point. - * - * Threads entering the barrier are halted until all threads reach the barrier. - * - * @see CountDownLatch - */ -class CyclicBarrier { -public: - /** - * Creates a cyclic barrier with a specified number of threads to synchronize. The minimum - * useful value is 2. A value of 0 is invalid and is silently changed to 1. - * @param num_threads Number of threads to synchronize. - */ - explicit CyclicBarrier(size_t num_threads) noexcept; - - /** - * @return The number of thread that are synchronized. - */ - size_t getThreadCount() const noexcept; - - /** - * @return Number of threads currently waiting on the barrier. - */ - size_t getWaitingThreadCount() const noexcept; - - /** - * Blocks until getThreadCount()-1 other threads reach await(). - */ - void await() noexcept; - - /** - * Resets the cyclic barrier to its original state and releases all waiting threads. - */ - void reset() noexcept; - - CyclicBarrier() = delete; - CyclicBarrier(const CyclicBarrier&) = delete; - CyclicBarrier& operator=(const CyclicBarrier&) = delete; - -private: - enum class State { - TRAP, RELEASE - }; - - const size_t m_num_threads; - mutable Mutex m_lock; - mutable Condition m_cv; - - State m_state = State::TRAP; - size_t m_trapped_threads = 0; - size_t m_released_threads = 0; -}; - -} // namespace utils - -#endif // TNT_UTILS_CYCLIC_BARRIER_H diff --git a/ios/include/utils/FixedCapacityVector.h b/ios/include/utils/FixedCapacityVector.h index 72efe30c..e24df575 100644 --- a/ios/include/utils/FixedCapacityVector.h +++ b/ios/include/utils/FixedCapacityVector.h @@ -20,12 +20,11 @@ #include #include -#include #include #include #include #include -#include +#include // TODO: is this necessary? #include #include @@ -397,7 +396,8 @@ private: SizeTypeWrapper() noexcept = default; SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default; explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { } - SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; } + SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; } + SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete; operator TYPE() const noexcept { return value; } }; diff --git a/ios/include/utils/Hash.h b/ios/include/utils/Hash.h deleted file mode 100644 index ee6e87f6..00000000 --- a/ios/include/utils/Hash.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_HASH_H -#define TNT_UTILS_HASH_H - -#include // for std::hash - -#include -#include - -namespace utils { -namespace hash { - -inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept { - uint32_t h = seed; - size_t i = wordCount; - do { - uint32_t k = *key++; - k *= 0xcc9e2d51u; - k = (k << 15u) | (k >> 17u); - k *= 0x1b873593u; - h ^= k; - h = (h << 13u) | (h >> 19u); - h = (h * 5u) + 0xe6546b64u; - } while (--i); - h ^= wordCount; - h ^= h >> 16u; - h *= 0x85ebca6bu; - h ^= h >> 13u; - h *= 0xc2b2ae35u; - h ^= h >> 16u; - return h; -} - -template -struct MurmurHashFn { - uint32_t operator()(const T& key) const noexcept { - static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4."); - return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0); - } -}; - -// combines two hashes together -template -inline void combine(size_t& seed, const T& v) noexcept { - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u); -} - -// combines two hashes together, faster but less good -template -inline void combine_fast(size_t& seed, const T& v) noexcept { - std::hash hasher; - seed ^= hasher(v) << 1u; -} - -} // namespace hash -} // namespace utils - -#endif // TNT_UTILS_HASH_H diff --git a/ios/include/utils/Invocable.h b/ios/include/utils/Invocable.h new file mode 100644 index 00000000..d2f69c36 --- /dev/null +++ b/ios/include/utils/Invocable.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_INVOKABLE_H +#define TNT_UTILS_INVOKABLE_H + +#include +#include + +#include + +namespace utils { + +/* + * Invocable is a move-only general purpose function wrapper. Instances can + * store and invoke lambda expressions and other function objects. + * + * It is similar to std::function, with the following differences: + * - Invocable is move only. + * - Invocable can capture move only types. + * - No conversion between 'compatible' functions. + * - No small buffer optimization + */ + +// Helper for enabling methods if Fn matches the function signature +// requirements of the Invocable type. +#if __cplusplus >= 201703L +// only available on C++17 and up +template +using EnableIfFnMatchesInvocable = + std::enable_if_t && + std::is_same_v>, int>; +#else +template +using EnableIfFnMatchesInvocable = std::enable_if_t; +#endif + +template +class Invocable; + +template +class Invocable { +public: + // Creates an Invocable that does not contain a functor. + // Will evaluate to false. + Invocable() = default; + + ~Invocable() noexcept; + + // Creates an Invocable from the functor passed in. + template = 0> + Invocable(Fn&& fn) noexcept; + + Invocable(const Invocable&) = delete; + Invocable(Invocable&& rhs) noexcept; + + Invocable& operator=(const Invocable&) = delete; + Invocable& operator=(Invocable&& rhs) noexcept; + + // Invokes the invocable with the args passed in. + // If the Invocable is empty, this will assert. + template + R operator()(OperatorArgs&& ... args); + template + R operator()(OperatorArgs&& ... args) const; + + // Evaluates to true if Invocable contains a functor, false otherwise. + explicit operator bool() const noexcept; + +private: + void* mInvocable = nullptr; + void (*mDeleter)(void*) = nullptr; + R (* mInvoker)(void*, Args...) = nullptr; +}; + +template +template> +Invocable::Invocable(Fn&& fn) noexcept + : mInvocable(new Fn(std::forward>(fn))), + mDeleter(+[](void* erased_invocable) { + auto typed_invocable = static_cast(erased_invocable); + delete typed_invocable; + }), + mInvoker(+[](void* erased_invocable, Args... args) -> R { + auto typed_invocable = static_cast(erased_invocable); + return (*typed_invocable)(std::forward(args)...); + }) +{ +} + +template +Invocable::~Invocable() noexcept { + if (mDeleter) { + mDeleter(mInvocable); + } +} + +template +Invocable::Invocable(Invocable&& rhs) noexcept + : mInvocable(rhs.mInvocable), + mDeleter(rhs.mDeleter), + mInvoker(rhs.mInvoker) { + rhs.mInvocable = nullptr; + rhs.mDeleter = nullptr; + rhs.mInvoker = nullptr; +} + +template +Invocable& Invocable::operator=(Invocable&& rhs) noexcept { + if (this != &rhs) { + mInvocable = rhs.mInvocable; + mDeleter = rhs.mDeleter; + mInvoker = rhs.mInvoker; + rhs.mInvocable = nullptr; + rhs.mDeleter = nullptr; + rhs.mInvoker = nullptr; + } + return *this; +} + +template +template +R Invocable::operator()(OperatorArgs&& ... args) { + assert(mInvoker && mInvocable); + return mInvoker(mInvocable, std::forward(args)...); +} + +template +template +R Invocable::operator()(OperatorArgs&& ... args) const { + assert(mInvoker && mInvocable); + return mInvoker(mInvocable, std::forward(args)...); +} + +template +Invocable::operator bool() const noexcept { + return mInvoker != nullptr && mInvocable != nullptr; +} + +} // namespace utils + +#endif // TNT_UTILS_INVOKABLE_H diff --git a/ios/include/utils/JobSystem.h b/ios/include/utils/JobSystem.h deleted file mode 100644 index 98fc96da..00000000 --- a/ios/include/utils/JobSystem.h +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_JOBSYSTEM_H -#define TNT_UTILS_JOBSYSTEM_H - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace utils { - -class JobSystem { - static constexpr size_t MAX_JOB_COUNT = 16384; - static_assert(MAX_JOB_COUNT <= 0x7FFE, "MAX_JOB_COUNT must be <= 0x7FFE"); - using WorkQueue = WorkStealingDequeue; - -public: - class Job; - - using JobFunc = void(*)(void*, JobSystem&, Job*); - - class alignas(CACHELINE_SIZE) Job { - public: - Job() noexcept {} /* = default; */ /* clang bug */ // NOLINT(modernize-use-equals-default,cppcoreguidelines-pro-type-member-init) - Job(const Job&) = delete; - Job(Job&&) = delete; - - private: - friend class JobSystem; - - // Size is chosen so that we can store at least std::function<> - // the alignas() qualifier ensures we're multiple of a cache-line. - static constexpr size_t JOB_STORAGE_SIZE_BYTES = - sizeof(std::function) > 48 ? sizeof(std::function) : 48; - static constexpr size_t JOB_STORAGE_SIZE_WORDS = - (JOB_STORAGE_SIZE_BYTES + sizeof(void*) - 1) / sizeof(void*); - - // keep it first, so it's correctly aligned with all architectures - // this is were we store the job's data, typically a std::function<> - // v7 | v8 - void* storage[JOB_STORAGE_SIZE_WORDS]; // 48 | 48 - JobFunc function; // 4 | 8 - uint16_t parent; // 2 | 2 - std::atomic runningJobCount = { 1 }; // 2 | 2 - mutable std::atomic refCount = { 1 }; // 2 | 2 - // 6 | 2 (padding) - // 64 | 64 - }; - - explicit JobSystem(size_t threadCount = 0, size_t adoptableThreadsCount = 1) noexcept; - - ~JobSystem(); - - // Make the current thread part of the thread pool. - void adopt(); - - // Remove this adopted thread from the parent. This is intended to be used for - // shutting down a JobSystem. In particular, this doesn't allow the parent to - // adopt more thread. - void emancipate(); - - - // If a parent is not specified when creating a job, that job will automatically take the - // root job as a parent. - // The root job is reset when waited on. - Job* setRootJob(Job* job) noexcept { return mRootJob = job; } - - // use setRootJob() instead - UTILS_DEPRECATED - Job* setMasterJob(Job* job) noexcept { return setRootJob(job); } - - - Job* create(Job* parent, JobFunc func) noexcept; - - // NOTE: All methods below must be called from the same thread and that thread must be - // owned by JobSystem's thread pool. - - /* - * Job creation examples: - * ---------------------- - * - * struct Functor { - * uintptr_t storage[6]; - * void operator()(JobSystem&, Jobsystem::Job*); - * } functor; - * - * struct Foo { - * uintptr_t storage[6]; - * void method(JobSystem&, Jobsystem::Job*); - * } foo; - * - * Functor and Foo size muse be <= uintptr_t[6] - * - * createJob() - * createJob(parent) - * createJob(parent, &foo) - * createJob(parent, foo) - * createJob(parent, std::ref(foo)) - * createJob(parent, functor) - * createJob(parent, std::ref(functor)) - * createJob(parent, [ up-to 6 uintptr_t ](JobSystem*, Jobsystem::Job*){ }) - * - * Utility functions: - * ------------------ - * These are less efficient, but handle any size objects using the heap if needed. - * (internally uses std::function<>), and don't require the callee to take - * a (JobSystem&, Jobsystem::Job*) as parameter. - * - * struct BigFoo { - * uintptr_t large[16]; - * void operator()(); - * void method(int answerToEverything); - * static void exec(BigFoo&) { } - * } bigFoo; - * - * jobs::createJob(js, parent, [ any-capture ](int answerToEverything){}, 42); - * jobs::createJob(js, parent, &BigFoo::method, &bigFoo, 42); - * jobs::createJob(js, parent, &BigFoo::exec, std::ref(bigFoo)); - * jobs::createJob(js, parent, bigFoo); - * jobs::createJob(js, parent, std::ref(bigFoo)); - * etc... - * - * struct SmallFunctor { - * uintptr_t storage[3]; - * void operator()(T* data, size_t count); - * } smallFunctor; - * - * jobs::parallel_for(js, data, count, [ up-to 3 uintptr_t ](T* data, size_t count) { }); - * jobs::parallel_for(js, data, count, smallFunctor); - * jobs::parallel_for(js, data, count, std::ref(smallFunctor)); - * - */ - - // creates an empty (no-op) job with an optional parent - Job* createJob(Job* parent = nullptr) noexcept { - return create(parent, nullptr); - } - - // creates a job from a KNOWN method pointer w/ object passed by pointer - // the caller must ensure the object will outlive the Job - template - Job* createJob(Job* parent, T* data) noexcept { - Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { - (*static_cast(user)->*method)(js, job); - }); - if (job) { - job->storage[0] = data; - } - return job; - } - - // creates a job from a KNOWN method pointer w/ object passed by value - template - Job* createJob(Job* parent, T data) noexcept { - static_assert(sizeof(data) <= sizeof(Job::storage), "user data too large"); - Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { - T* that = static_cast(user); - (that->*method)(js, job); - that->~T(); - }); - if (job) { - new(job->storage) T(std::move(data)); - } - return job; - } - - // creates a job from a functor passed by value - template - Job* createJob(Job* parent, T functor) noexcept { - static_assert(sizeof(functor) <= sizeof(Job::storage), "functor too large"); - Job* job = create(parent, [](void* user, JobSystem& js, Job* job){ - T& that = *static_cast(user); - that(js, job); - that.~T(); - }); - if (job) { - new(job->storage) T(std::move(functor)); - } - return job; - } - - - /* - * Jobs are normally finished automatically, this can be used to cancel a job before it is run. - * - * Never use this once a flavor of run() has been called. - */ - void cancel(Job*& job) noexcept; - - /* - * Adds a reference to a Job. - * - * This allows the caller to waitAndRelease() on this job from multiple threads. - * Use runAndWait() if waiting from multiple threads is not needed. - * - * This job MUST BE waited on with waitAndRelease(), or released with release(). - */ - Job* retain(Job* job) noexcept; - - /* - * Releases a reference from a Job obtained with runAndRetain() or a call to retain(). - * - * The job can't be used after this call. - */ - void release(Job*& job) noexcept; - void release(Job*&& job) noexcept { - Job* p = job; - release(p); - } - - /* - * Add job to this thread's execution queue. It's reference will drop automatically. - * Current thread must be owned by JobSystem's thread pool. See adopt(). - * - * The job can't be used after this call. - */ - void run(Job*& job) noexcept; - void run(Job*&& job) noexcept { // allows run(createJob(...)); - Job* p = job; - run(p); - } - - void signal() noexcept; - - /* - * Add job to this thread's execution queue and and keep a reference to it. - * Current thread must be owned by JobSystem's thread pool. See adopt(). - * - * This job MUST BE waited on with wait(), or released with release(). - */ - Job* runAndRetain(Job* job) noexcept; - - /* - * Wait on a job and destroys it. - * Current thread must be owned by JobSystem's thread pool. See adopt(). - * - * The job must first be obtained from runAndRetain() or retain(). - * The job can't be used after this call. - */ - void waitAndRelease(Job*& job) noexcept; - - /* - * Runs and wait for a job. This is equivalent to calling - * runAndRetain(job); - * wait(job); - * - * The job can't be used after this call. - */ - void runAndWait(Job*& job) noexcept; - void runAndWait(Job*&& job) noexcept { // allows runAndWait(createJob(...)); - Job* p = job; - runAndWait(p); - } - - // for debugging - friend utils::io::ostream& operator << (utils::io::ostream& out, JobSystem const& js); - - - // utility functions... - - // set the name of the current thread (on OSes that support it) - static void setThreadName(const char* threadName) noexcept; - - enum class Priority { - NORMAL, - DISPLAY, - URGENT_DISPLAY - }; - - static void setThreadPriority(Priority priority) noexcept; - static void setThreadAffinityById(size_t id) noexcept; - - size_t getParallelSplitCount() const noexcept { - return mParallelSplitCount; - } - -private: - // this is just to avoid using std::default_random_engine, since we're in a public header. - class default_random_engine { - static constexpr uint32_t m = 0x7fffffffu; - uint32_t mState; // must be 0 < seed < 0x7fffffff - public: - inline constexpr explicit default_random_engine(uint32_t seed = 1u) noexcept - : mState(((seed % m) == 0u) ? 1u : seed % m) { - } - inline uint32_t operator()() noexcept { - return mState = uint32_t((uint64_t(mState) * 48271u) % m); - } - }; - - struct alignas(CACHELINE_SIZE) ThreadState { // this causes 40-bytes padding - // make sure storage is cache-line aligned - WorkQueue workQueue; - - // these are not accessed by the worker threads - alignas(CACHELINE_SIZE) // this causes 56-bytes padding - JobSystem* js; - std::thread thread; - default_random_engine rndGen; - uint32_t id; - }; - - static_assert(sizeof(ThreadState) % CACHELINE_SIZE == 0, - "ThreadState doesn't align to a cache line"); - - ThreadState& getState() noexcept; - - void incRef(Job const* job) noexcept; - void decRef(Job const* job) noexcept; - - Job* allocateJob() noexcept; - JobSystem::ThreadState* getStateToStealFrom(JobSystem::ThreadState& state) noexcept; - bool hasJobCompleted(Job const* job) noexcept; - - void requestExit() noexcept; - bool exitRequested() const noexcept; - bool hasActiveJobs() const noexcept; - - void loop(ThreadState* state) noexcept; - bool execute(JobSystem::ThreadState& state) noexcept; - Job* steal(JobSystem::ThreadState& state) noexcept; - void finish(Job* job) noexcept; - - void put(WorkQueue& workQueue, Job* job) noexcept { - assert(job); - size_t index = job - mJobStorageBase; - assert(index >= 0 && index < MAX_JOB_COUNT); - workQueue.push(uint16_t(index + 1)); - } - - Job* pop(WorkQueue& workQueue) noexcept { - size_t index = workQueue.pop(); - assert(index <= MAX_JOB_COUNT); - return !index ? nullptr : &mJobStorageBase[index - 1]; - } - - Job* steal(WorkQueue& workQueue) noexcept { - size_t index = workQueue.steal(); - assert(index <= MAX_JOB_COUNT); - return !index ? nullptr : &mJobStorageBase[index - 1]; - } - - void wait(std::unique_lock& lock, Job* job = nullptr) noexcept; - void wakeAll() noexcept; - void wakeOne() noexcept; - - // these have thread contention, keep them together - utils::Mutex mWaiterLock; - utils::Condition mWaiterCondition; - - std::atomic mActiveJobs = { 0 }; - utils::Arena, LockingPolicy::NoLock> mJobPool; - - template - using aligned_vector = std::vector>; - - // these are essentially const, make sure they're on a different cache-lines than the - // read-write atomics. - // We can't use "alignas(CACHELINE_SIZE)" because the standard allocator can't make this - // guarantee. - char padding[CACHELINE_SIZE]; - - alignas(16) // at least we align to half (or quarter) cache-line - aligned_vector mThreadStates; // actual data is stored offline - std::atomic mExitRequested = { false }; // this one is almost never written - std::atomic mAdoptedThreads = { 0 }; // this one is almost never written - Job* const mJobStorageBase; // Base for conversion to indices - uint16_t mThreadCount = 0; // total # of threads in the pool - uint8_t mParallelSplitCount = 0; // # of split allowable in parallel_for - Job* mRootJob = nullptr; - - utils::SpinLock mThreadMapLock; // this should have very little contention - tsl::robin_map mThreadMap; -}; - -// ------------------------------------------------------------------------------------------------- -// Utility functions built on top of JobSystem - -namespace jobs { - -// These are convenience C++11 style job creation methods that support lambdas -// -// IMPORTANT: these are less efficient to call and may perform heap allocation -// depending on the capture and parameters -// -template -JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent, - CALLABLE&& func, ARGS&&... args) noexcept { - struct Data { - std::function f; - // Renaming the method below could cause an Arrested Development. - void gob(JobSystem&, JobSystem::Job*) noexcept { f(); } - } user{ std::bind(std::forward(func), - std::forward(args)...) }; - return js.createJob(parent, std::move(user)); -} - -template::type>::value - >::type -> -JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent, - CALLABLE&& func, T&& o, ARGS&&... args) noexcept { - struct Data { - std::function f; - // Renaming the method below could cause an Arrested Development. - void gob(JobSystem&, JobSystem::Job*) noexcept { f(); } - } user{ std::bind(std::forward(func), std::forward(o), - std::forward(args)...) }; - return js.createJob(parent, std::move(user)); -} - - -namespace details { - -template -struct ParallelForJobData { - using SplitterType = S; - using Functor = F; - using JobData = ParallelForJobData; - using size_type = uint32_t; - - ParallelForJobData(size_type start, size_type count, uint8_t splits, - Functor functor, - const SplitterType& splitter) noexcept - : start(start), count(count), - functor(std::move(functor)), - splits(splits), - splitter(splitter) { - } - - void parallelWithJobs(JobSystem& js, JobSystem::Job* parent) noexcept { - assert(parent); - - // this branch is often miss-predicted (it both sides happen 50% of the calls) -right_side: - if (splitter.split(splits, count)) { - const size_type lc = count / 2; - JobData ld(start, lc, splits + uint8_t(1), functor, splitter); - JobSystem::Job* l = js.createJob(parent, std::move(ld)); - if (UTILS_UNLIKELY(l == nullptr)) { - // couldn't create a job, just pretend we're done splitting - goto execute; - } - - // start the left side before attempting the right side, so we parallelize in case - // of job creation failure -- rare, but still. - js.run(l); - - // don't spawn a job for the right side, just reuse us -- spawning jobs is more - // costly than we'd like. - start += lc; - count -= lc; - ++splits; - goto right_side; - - } else { -execute: - // we're done splitting, do the real work here! - functor(start, count); - } - } - -private: - size_type start; // 4 - size_type count; // 4 - Functor functor; // ? - uint8_t splits; // 1 - SplitterType splitter; // 1 -}; - -} // namespace details - - -// parallel jobs with start/count indices -template -JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, - uint32_t start, uint32_t count, F functor, const S& splitter) noexcept { - using JobData = details::ParallelForJobData; - JobData jobData(start, count, 0, std::move(functor), splitter); - return js.createJob(parent, std::move(jobData)); -} - -// parallel jobs with pointer/count -template -JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, - T* data, uint32_t count, F functor, const S& splitter) noexcept { - auto user = [data, f = std::move(functor)](uint32_t s, uint32_t c) { - f(data + s, c); - }; - using JobData = details::ParallelForJobData; - JobData jobData(0, count, 0, std::move(user), splitter); - return js.createJob(parent, std::move(jobData)); -} - -// parallel jobs on a Slice<> -template -JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, - utils::Slice slice, F functor, const S& splitter) noexcept { - return parallel_for(js, parent, slice.data(), slice.size(), functor, splitter); -} - - -template -class CountSplitter { -public: - bool split(size_t splits, size_t count) const noexcept { - return (splits < MAX_SPLITS && count >= COUNT * 2); - } -}; - -} // namespace jobs -} // namespace utils - -#endif // TNT_UTILS_JOBSYSTEM_H diff --git a/ios/include/utils/Panic.h b/ios/include/utils/Panic.h index 2be50d18..df24cac2 100644 --- a/ios/include/utils/Panic.h +++ b/ios/include/utils/Panic.h @@ -424,8 +424,10 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { #ifndef NDEBUG # define PANIC_FILE(F) (F) +# define PANIC_FUNCTION __PRETTY_FUNCTION__ #else # define PANIC_FILE(F) "" +# define PANIC_FUNCTION __func__ #endif /** @@ -433,7 +435,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define PANIC_PRECONDITION(format, ...) \ - ::utils::PreconditionPanic::panic(__PRETTY_FUNCTION__, \ + ::utils::PreconditionPanic::panic(PANIC_FUNCTION, \ PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) /** @@ -441,7 +443,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define PANIC_POSTCONDITION(format, ...) \ - ::utils::PostconditionPanic::panic(__PRETTY_FUNCTION__, \ + ::utils::PostconditionPanic::panic(PANIC_FUNCTION, \ PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) /** @@ -449,7 +451,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define PANIC_ARITHMETIC(format, ...) \ - ::utils::ArithmeticPanic::panic(__PRETTY_FUNCTION__, \ + ::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \ PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) /** @@ -457,7 +459,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define PANIC_LOG(format, ...) \ - ::utils::details::panicLog(__PRETTY_FUNCTION__, \ + ::utils::details::panicLog(PANIC_FUNCTION, \ PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) /** diff --git a/ios/include/utils/PrivateImplementation-impl.h b/ios/include/utils/PrivateImplementation-impl.h new file mode 100644 index 00000000..3488b5ae --- /dev/null +++ b/ios/include/utils/PrivateImplementation-impl.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_PRIVATEIMPLEMENTATION_IMPL_H +#define UTILS_PRIVATEIMPLEMENTATION_IMPL_H + +/* + * This looks like a header file, but really it acts as a .cpp, because it's used to + * explicitly instantiate the methods of PrivateImplementation<> + */ + +#include + +#include + +namespace utils { + +template +PrivateImplementation::PrivateImplementation() noexcept + : mImpl(new T) { +} + +template +template +PrivateImplementation::PrivateImplementation(ARGS&& ... args) noexcept + : mImpl(new T(std::forward(args)...)) { +} + +template +PrivateImplementation::~PrivateImplementation() noexcept { + delete mImpl; +} + +#ifndef UTILS_PRIVATE_IMPLEMENTATION_NON_COPYABLE + +template +PrivateImplementation::PrivateImplementation(PrivateImplementation const& rhs) noexcept + : mImpl(new T(*rhs.mImpl)) { +} + +template +PrivateImplementation& PrivateImplementation::operator=(PrivateImplementation const& rhs) noexcept { + if (this != &rhs) { + *mImpl = *rhs.mImpl; + } + return *this; +} + +#endif + +} // namespace utils + +#endif // UTILS_PRIVATEIMPLEMENTATION_IMPL_H diff --git a/ios/include/utils/PrivateImplementation.h b/ios/include/utils/PrivateImplementation.h new file mode 100644 index 00000000..7cb510fa --- /dev/null +++ b/ios/include/utils/PrivateImplementation.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_PRIVATEIMPLEMENTATION_H +#define UTILS_PRIVATEIMPLEMENTATION_H + +#include + +#include + +namespace utils { + +/** + * \privatesection + * PrivateImplementation is used to hide the implementation details of a class and ensure a higher + * level of backward binary compatibility. + * The actual implementation is in src/PrivateImplementation-impl.h" + */ +template +class PrivateImplementation { +public: + // none of these methods must be implemented inline because it's important that their + // implementation be hidden from the public headers. + template + explicit PrivateImplementation(ARGS&& ...) noexcept; + PrivateImplementation() noexcept; + ~PrivateImplementation() noexcept; + PrivateImplementation(PrivateImplementation const& rhs) noexcept; + PrivateImplementation& operator = (PrivateImplementation const& rhs) noexcept; + + // move ctor and copy operator can be implemented inline and don't need to be exported + PrivateImplementation(PrivateImplementation&& rhs) noexcept : mImpl(rhs.mImpl) { rhs.mImpl = nullptr; } + PrivateImplementation& operator = (PrivateImplementation&& rhs) noexcept { + auto temp = mImpl; + mImpl = rhs.mImpl; + rhs.mImpl = temp; + return *this; + } + +protected: + T* mImpl = nullptr; + inline T* operator->() noexcept { return mImpl; } + inline T const* operator->() const noexcept { return mImpl; } +}; + +} // namespace utils + +#endif // UTILS_PRIVATEIMPLEMENTATION_H diff --git a/ios/include/utils/Profiler.h b/ios/include/utils/Profiler.h deleted file mode 100644 index f41bd1e9..00000000 --- a/ios/include/utils/Profiler.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_PROFILER_H -#define TNT_UTILS_PROFILER_H - -#include -#include -#include - -#include // note: This is safe (only used inline) - -#if defined(__linux__) -# include -# include -# include -#endif - -#include - -namespace utils { - -class Profiler { -public: - enum { - INSTRUCTIONS = 0, // must be zero - CPU_CYCLES = 1, - DCACHE_REFS = 2, - DCACHE_MISSES = 3, - BRANCHES = 4, - BRANCH_MISSES = 5, - ICACHE_REFS = 6, - ICACHE_MISSES = 7, - - // Must be last one - EVENT_COUNT - }; - - enum { - EV_CPU_CYCLES = 1u << CPU_CYCLES, - EV_L1D_REFS = 1u << DCACHE_REFS, - EV_L1D_MISSES = 1u << DCACHE_MISSES, - EV_BPU_REFS = 1u << BRANCHES, - EV_BPU_MISSES = 1u << BRANCH_MISSES, - EV_L1I_REFS = 1u << ICACHE_REFS, - EV_L1I_MISSES = 1u << ICACHE_MISSES, - // helpers - EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES, - EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES, - EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES, - }; - - Profiler() noexcept; // must call resetEvents() - explicit Profiler(uint32_t eventMask) noexcept; - ~Profiler() noexcept; - - Profiler(const Profiler& rhs) = delete; - Profiler(Profiler&& rhs) = delete; - Profiler& operator=(const Profiler& rhs) = delete; - Profiler& operator=(Profiler&& rhs) = delete; - - // selects which events are enabled. - uint32_t resetEvents(uint32_t eventMask) noexcept; - - uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; } - - // could return false if performance counters are not supported/enabled - bool isValid() const { return mCountersFd[0] >= 0; } - - class Counters { - friend class Profiler; - uint64_t nr; - uint64_t time_enabled; - uint64_t time_running; - struct { - uint64_t value; - uint64_t id; - } counters[Profiler::EVENT_COUNT]; - - friend Counters operator-(Counters lhs, const Counters& rhs) noexcept { - lhs.nr -= rhs.nr; - lhs.time_enabled -= rhs.time_enabled; - lhs.time_running -= rhs.time_running; - for (size_t i = 0; i < EVENT_COUNT; ++i) { - lhs.counters[i].value -= rhs.counters[i].value; - } - return lhs; - } - - public: - uint64_t getInstructions() const { return counters[INSTRUCTIONS].value; } - uint64_t getCpuCycles() const { return counters[CPU_CYCLES].value; } - uint64_t getL1DReferences() const { return counters[DCACHE_REFS].value; } - uint64_t getL1DMisses() const { return counters[DCACHE_MISSES].value; } - uint64_t getL1IReferences() const { return counters[ICACHE_REFS].value; } - uint64_t getL1IMisses() const { return counters[ICACHE_MISSES].value; } - uint64_t getBranchInstructions() const { return counters[BRANCHES].value; } - uint64_t getBranchMisses() const { return counters[BRANCH_MISSES].value; } - - std::chrono::duration getWallTime() const { - return std::chrono::duration(time_enabled); - } - - std::chrono::duration getRunningTime() const { - return std::chrono::duration(time_running); - } - - double getIPC() const noexcept { - uint64_t cpuCycles = getCpuCycles(); - uint64_t instructions = getInstructions(); - return double(instructions) / double(cpuCycles); - } - - double getCPI() const noexcept { - uint64_t cpuCycles = getCpuCycles(); - uint64_t instructions = getInstructions(); - return double(cpuCycles) / double(instructions); - } - - double getL1DMissRate() const noexcept { - uint64_t cacheReferences = getL1DReferences(); - uint64_t cacheMisses = getL1DMisses(); - return double(cacheMisses) / double(cacheReferences); - } - - double getL1DHitRate() const noexcept { - return 1.0 - getL1DMissRate(); - } - - double getL1IMissRate() const noexcept { - uint64_t cacheReferences = getL1IReferences(); - uint64_t cacheMisses = getL1IMisses(); - return double(cacheMisses) / double(cacheReferences); - } - - double getL1IHitRate() const noexcept { - return 1.0 - getL1IMissRate(); - } - - double getBranchMissRate() const noexcept { - uint64_t branchReferences = getBranchInstructions(); - uint64_t branchMisses = getBranchMisses(); - return double(branchMisses) / double(branchReferences); - } - - double getBranchHitRate() const noexcept { - return 1.0 - getBranchMissRate(); - } - - double getMPKI(uint64_t misses) const noexcept { - return (misses * 1000.0) / getInstructions(); - } - }; - -#if defined(__linux__) - - void reset() noexcept { - int fd = mCountersFd[0]; - ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); - } - - void start() noexcept { - int fd = mCountersFd[0]; - ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); - } - - void stop() noexcept { - int fd = mCountersFd[0]; - ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); - } - - Counters readCounters() noexcept; - -#else // !__linux__ - - void reset() noexcept { } - void start() noexcept { } - void stop() noexcept { } - Counters readCounters() noexcept { return {}; } - -#endif // __linux__ - - bool hasBranchRates() const noexcept { - return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0); - } - - bool hasICacheRates() const noexcept { - return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0); - } - -private: - UTILS_UNUSED uint8_t mIds[EVENT_COUNT] = {}; - int mCountersFd[EVENT_COUNT]; - uint32_t mEnabledEvents = 0; -}; - -} // namespace utils - -#endif // TNT_UTILS_PROFILER_H diff --git a/ios/include/utils/Range.h b/ios/include/utils/Range.h deleted file mode 100644 index ba922bd3..00000000 --- a/ios/include/utils/Range.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_RANGE_H -#define TNT_UTILS_RANGE_H - -#include - -#include -#include - -#include - -namespace utils { - -template -struct Range { - using value_type = T; - T first = 0; - T last = 0; - - size_t size() const noexcept { return last - first; } - bool empty() const noexcept { return !size(); } - - class const_iterator { - friend struct Range; - T value = {}; - - public: - const_iterator() noexcept = default; - explicit const_iterator(T value) noexcept : value(value) {} - - using value_type = T; - using pointer = value_type*; - using difference_type = ptrdiff_t; - using iterator_category = std::random_access_iterator_tag; - - - const value_type operator*() const { return value; } - const value_type operator[](size_t n) const { return value + n; } - - const_iterator& operator++() { ++value; return *this; } - const_iterator& operator--() { --value; return *this; } - - const const_iterator operator++(int) { const_iterator t(value); value++; return t; } - const const_iterator operator--(int) { const_iterator t(value); value--; return t; } - - const_iterator operator+(size_t rhs) const { return { value + rhs }; } - const_iterator operator+(size_t rhs) { return { value + rhs }; } - const_iterator operator-(size_t rhs) const { return { value - rhs }; } - - difference_type operator-(const_iterator const& rhs) const { return value - rhs.value; } - - bool operator==(const_iterator const& rhs) const { return (value == rhs.value); } - bool operator!=(const_iterator const& rhs) const { return (value != rhs.value); } - bool operator>=(const_iterator const& rhs) const { return (value >= rhs.value); } - bool operator> (const_iterator const& rhs) const { return (value > rhs.value); } - bool operator<=(const_iterator const& rhs) const { return (value <= rhs.value); } - bool operator< (const_iterator const& rhs) const { return (value < rhs.value); } - }; - - const_iterator begin() noexcept { return const_iterator{ first }; } - const_iterator end() noexcept { return const_iterator{ last }; } - const_iterator begin() const noexcept { return const_iterator{ first }; } - const_iterator end() const noexcept { return const_iterator{ last }; } - - const_iterator front() const noexcept { return const_iterator{ first }; } - const_iterator back() const noexcept { return const_iterator{ last - 1 }; } -}; - -} // namespace utils - -#endif // TNT_UTILS_RANGE_H diff --git a/ios/include/utils/SingleInstanceComponentManager.h b/ios/include/utils/SingleInstanceComponentManager.h index 142869b5..574d14a7 100644 --- a/ios/include/utils/SingleInstanceComponentManager.h +++ b/ios/include/utils/SingleInstanceComponentManager.h @@ -237,7 +237,7 @@ protected: size_t count = getComponentCount(); size_t aliveInARow = 0; default_random_engine& rng = mRng; - #pragma nounroll + UTILS_NOUNROLL while (count && aliveInARow < ratio) { // note: using the modulo favorizes lower number size_t i = rng() % count; diff --git a/ios/include/utils/Slice.h b/ios/include/utils/Slice.h index eabf7979..eb825e3c 100644 --- a/ios/include/utils/Slice.h +++ b/ios/include/utils/Slice.h @@ -17,8 +17,8 @@ #ifndef TNT_UTILS_SLICE_H #define TNT_UTILS_SLICE_H -#include #include +#include #include #include diff --git a/ios/include/utils/Systrace.h b/ios/include/utils/Systrace.h deleted file mode 100644 index 67553f77..00000000 --- a/ios/include/utils/Systrace.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_SYSTRACE_H -#define TNT_UTILS_SYSTRACE_H - - -#define SYSTRACE_TAG_NEVER (0) -#define SYSTRACE_TAG_ALWAYS (1<<0) -#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles -#define SYSTRACE_TAG_JOBSYSTEM (1<<2) - - -#if defined(__ANDROID__) - -#include - -#include -#include -#include - -#include - -/* - * The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined - * before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used. - */ - -#ifndef SYSTRACE_TAG -#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS) -#endif - -// enable tracing -#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG) - -// disable tracing -#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG) - - -/** - * Creates a Systrace context in the current scope. needed for calling all other systrace - * commands below. - */ -#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___tracer(SYSTRACE_TAG) - - -// SYSTRACE_NAME traces the beginning and end of the current scope. To trace -// the correct start and end times this macro should be declared first in the -// scope body. -// It also automatically creates a Systrace context -#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name) - -// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name. -#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__) - -#define SYSTRACE_NAME_BEGIN(name) \ - ___tracer.traceBegin(SYSTRACE_TAG, name) - -#define SYSTRACE_NAME_END() \ - ___tracer.traceEnd(SYSTRACE_TAG) - - -/** - * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END - * contexts, asynchronous events do not need to be nested. The name describes - * the event, and the cookie provides a unique identifier for distinguishing - * simultaneous events. The name and cookie used to begin an event must be - * used to end it. - */ -#define SYSTRACE_ASYNC_BEGIN(name, cookie) \ - ___tracer.asyncBegin(SYSTRACE_TAG, name, cookie) - -/** - * Trace the end of an asynchronous event. - * This should have a corresponding SYSTRACE_ASYNC_BEGIN. - */ -#define SYSTRACE_ASYNC_END(name, cookie) \ - ___tracer.asyncEnd(SYSTRACE_TAG, name, cookie) - -/** - * Traces an integer counter value. name is used to identify the counter. - * This can be used to track how a value changes over time. - */ -#define SYSTRACE_VALUE32(name, val) \ - ___tracer.value(SYSTRACE_TAG, name, int32_t(val)) - -#define SYSTRACE_VALUE64(name, val) \ - ___tracer.value(SYSTRACE_TAG, name, int64_t(val)) - -// ------------------------------------------------------------------------------------------------ -// No user serviceable code below... -// ------------------------------------------------------------------------------------------------ - -namespace utils { -namespace details { - -class Systrace { -public: - - enum tags { - NEVER = SYSTRACE_TAG_NEVER, - ALWAYS = SYSTRACE_TAG_ALWAYS, - FILAMENT = SYSTRACE_TAG_FILAMENT, - JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM - // we could define more TAGS here, as we need them. - }; - - Systrace(uint32_t tag) noexcept { - if (tag) init(tag); - } - - static void enable(uint32_t tags) noexcept; - static void disable(uint32_t tags) noexcept; - - - inline void traceBegin(uint32_t tag, const char* name) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - beginSection(this, name); - } - } - - inline void traceEnd(uint32_t tag) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - endSection(this); - } - } - - inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - beginAsyncSection(this, name, cookie); - } - } - - inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - endAsyncSection(this, name, cookie); - } - } - - inline void value(uint32_t tag, const char* name, int32_t value) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - setCounter(this, name, value); - } - } - - inline void value(uint32_t tag, const char* name, int64_t value) noexcept { - if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - setCounter(this, name, value); - } - } - -private: - friend class ScopedTrace; - - // whether tracing is supported at all by the platform - - using ATrace_isEnabled_t = bool (*)(void); - using ATrace_beginSection_t = void (*)(const char* sectionName); - using ATrace_endSection_t = void (*)(void); - using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); - using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); - using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue); - - struct GlobalState { - bool isTracingAvailable; - std::atomic isTracingEnabled; - int markerFd; - - ATrace_isEnabled_t ATrace_isEnabled; - ATrace_beginSection_t ATrace_beginSection; - ATrace_endSection_t ATrace_endSection; - ATrace_beginAsyncSection_t ATrace_beginAsyncSection; - ATrace_endAsyncSection_t ATrace_endAsyncSection; - ATrace_setCounter_t ATrace_setCounter; - - void (*beginSection)(Systrace* that, const char* name); - void (*endSection)(Systrace* that); - void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); - void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); - void (*setCounter)(Systrace* that, const char* name, int64_t value); - }; - - static GlobalState sGlobalState; - - - // per-instance versions for better performance - ATrace_isEnabled_t ATrace_isEnabled; - ATrace_beginSection_t ATrace_beginSection; - ATrace_endSection_t ATrace_endSection; - ATrace_beginAsyncSection_t ATrace_beginAsyncSection; - ATrace_endAsyncSection_t ATrace_endAsyncSection; - ATrace_setCounter_t ATrace_setCounter; - - void (*beginSection)(Systrace* that, const char* name); - void (*endSection)(Systrace* that); - void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); - void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); - void (*setCounter)(Systrace* that, const char* name, int64_t value); - - void init(uint32_t tag) noexcept; - - // cached values for faster access, no need to be initialized - bool mIsTracingEnabled; - int mMarkerFd = -1; - pid_t mPid; - - static void setup() noexcept; - static void init_once() noexcept; - static bool isTracingEnabled(uint32_t tag) noexcept; - - static void begin_body(int fd, int pid, const char* name) noexcept; - static void end_body(int fd, int pid) noexcept; - static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept; - static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept; - static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept; -}; - -// ------------------------------------------------------------------------------------------------ - -class ScopedTrace { -public: - // we don't inline this because it's relatively heavy due to a global check - ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mTag(tag) { - mTrace.traceBegin(tag, name); - } - - inline ~ScopedTrace() noexcept { - mTrace.traceEnd(mTag); - } - - inline void value(uint32_t tag, const char* name, int32_t v) noexcept { - mTrace.value(tag, name, v); - } - - inline void value(uint32_t tag, const char* name, int64_t v) noexcept { - mTrace.value(tag, name, v); - } - -private: - Systrace mTrace; - const uint32_t mTag; -}; - -} // namespace details -} // namespace utils - -// ------------------------------------------------------------------------------------------------ -#else // !ANDROID -// ------------------------------------------------------------------------------------------------ - -#define SYSTRACE_ENABLE() -#define SYSTRACE_DISABLE() -#define SYSTRACE_CONTEXT() -#define SYSTRACE_NAME(name) -#define SYSTRACE_NAME_BEGIN(name) -#define SYSTRACE_NAME_END() -#define SYSTRACE_CALL() -#define SYSTRACE_ASYNC_BEGIN(name, cookie) -#define SYSTRACE_ASYNC_END(name, cookie) -#define SYSTRACE_VALUE32(name, val) -#define SYSTRACE_VALUE64(name, val) - -#endif // ANDROID - -#endif // TNT_UTILS_SYSTRACE_H diff --git a/ios/include/utils/ThermalManager.h b/ios/include/utils/ThermalManager.h deleted file mode 100644 index fc38d88e..00000000 --- a/ios/include/utils/ThermalManager.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_THERMALMANAGER_H -#define TNT_UTILS_THERMALMANAGER_H - -#if defined(__ANDROID__) -#include -#else -#include -#endif - -#endif // TNT_UTILS_THERMALMANAGER_H diff --git a/ios/include/utils/WorkStealingDequeue.h b/ios/include/utils/WorkStealingDequeue.h deleted file mode 100644 index 73b1ce6e..00000000 --- a/ios/include/utils/WorkStealingDequeue.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_WORKSTEALINGDEQUEUE_H -#define TNT_UTILS_WORKSTEALINGDEQUEUE_H - -#include - -#include -#include - -namespace utils { - -/* - * A templated, lockless, fixed-size work-stealing dequeue - * - * - * top bottom - * v v - * |----|----|----|----|----|----| - * steal() push(), pop() - * any thread main thread - * - * - */ -template -class WorkStealingDequeue { - static_assert(!(COUNT & (COUNT - 1)), "COUNT must be a power of two"); - static constexpr size_t MASK = COUNT - 1; - - // mTop and mBottom must be signed integers. We use 64-bits atomics so we don't have - // to worry about wrapping around. - using index_t = int64_t; - - std::atomic mTop = { 0 }; // written/read in pop()/steal() - std::atomic mBottom = { 0 }; // written only in pop(), read in push(), steal() - - TYPE mItems[COUNT]; - - // NOTE: it's not safe to return a reference because getItemAt() can be called - // concurrently and the caller could std::move() the item unsafely. - TYPE getItemAt(index_t index) noexcept { return mItems[index & MASK]; } - - void setItemAt(index_t index, TYPE item) noexcept { mItems[index & MASK] = item; } - -public: - using value_type = TYPE; - - inline void push(TYPE item) noexcept; - inline TYPE pop() noexcept; - inline TYPE steal() noexcept; - - size_t getSize() const noexcept { return COUNT; } - - // for debugging only... - size_t getCount() const noexcept { - index_t bottom = mBottom.load(std::memory_order_relaxed); - index_t top = mTop.load(std::memory_order_relaxed); - return bottom - top; - } -}; - -/* - * Adds an item at the BOTTOM of the queue. - * - * Must be called from the main thread. - */ -template -void WorkStealingDequeue::push(TYPE item) noexcept { - // std::memory_order_relaxed is sufficient because this load doesn't acquire anything from - // another thread. mBottom is only written in pop() which cannot be concurrent with push() - index_t bottom = mBottom.load(std::memory_order_relaxed); - setItemAt(bottom, item); - - // std::memory_order_release is used because we release the item we just pushed to other - // threads which are calling steal(). - mBottom.store(bottom + 1, std::memory_order_release); -} - -/* - * Removes an item from the BOTTOM of the queue. - * - * Must be called from the main thread. - */ -template -TYPE WorkStealingDequeue::pop() noexcept { - // std::memory_order_seq_cst is needed to guarantee ordering in steal() - // Note however that this is not a typical acquire/release operation: - // - not acquire because mBottom is only written in push() which is not concurrent - // - not release because we're not publishing anything to steal() here - // - // QUESTION: does this prevent mTop load below to be reordered before the "store" part of - // fetch_sub()? Hopefully it does. If not we'd need a full memory barrier. - // - index_t bottom = mBottom.fetch_sub(1, std::memory_order_seq_cst) - 1; - - // bottom could be -1 if we tried to pop() from an empty queue. This will be corrected below. - assert( bottom >= -1 ); - - // std::memory_order_seq_cst is needed to guarantee ordering in steal() - // Note however that this is not a typical acquire operation - // (i.e. other thread's writes of mTop don't publish data) - index_t top = mTop.load(std::memory_order_seq_cst); - - if (top < bottom) { - // Queue isn't empty and it's not the last item, just return it, this is the common case. - return getItemAt(bottom); - } - - TYPE item{}; - if (top == bottom) { - // we just took the last item - item = getItemAt(bottom); - - // Because we know we took the last item, we could be racing with steal() -- the last - // item being both at the top and bottom of the queue. - // We resolve this potential race by also stealing that item from ourselves. - if (mTop.compare_exchange_strong(top, top + 1, - std::memory_order_seq_cst, - std::memory_order_relaxed)) { - // success: we stole our last item from ourself, meaning that a concurrent steal() - // would have failed. - // mTop now equals top + 1, we adjust top to make the queue empty. - top++; - } else { - // failure: mTop was not equal to top, which means the item was stolen under our feet. - // top now equals to mTop. Simply discard the item we just popped. - // The queue is now empty. - item = TYPE(); - } - } else { - // We could be here if the item was stolen just before we read mTop, we'll adjust - // mBottom below. - assert(top - bottom == 1); - } - - // std::memory_order_relaxed used because we're not publishing any data. - // no concurrent writes to mBottom possible, it's always safe to write mBottom. - mBottom.store(top, std::memory_order_relaxed); - return item; -} - -/* - * Steals an item from the TOP of another thread's queue. - * - * This can be called concurrently with steal(), push() or pop() - * - * steal() never fails, either there is an item and it atomically takes it, or there isn't and - * it returns an empty item. - */ -template -TYPE WorkStealingDequeue::steal() noexcept { - while (true) { - /* - * Note: A Key component of this algorithm is that mTop is read before mBottom here - * (and observed as such in other threads) - */ - - // std::memory_order_seq_cst is needed to guarantee ordering in pop() - // Note however that this is not a typical acquire operation - // (i.e. other thread's writes of mTop don't publish data) - index_t top = mTop.load(std::memory_order_seq_cst); - - // std::memory_order_acquire is needed because we're acquiring items published in push(). - // std::memory_order_seq_cst is needed to guarantee ordering in pop() - index_t bottom = mBottom.load(std::memory_order_seq_cst); - - if (top >= bottom) { - // queue is empty - return TYPE(); - } - - // The queue isn't empty - TYPE item(getItemAt(top)); - if (mTop.compare_exchange_strong(top, top + 1, - std::memory_order_seq_cst, - std::memory_order_relaxed)) { - // success: we stole an item, just return it. - return item; - } - // failure: the item we just tried to steal was pop()'ed under our feet, - // simply discard it; nothing to do -- it's okay to try again. - } -} - - -} // namespace utils - -#endif // TNT_UTILS_WORKSTEALINGDEQUEUE_H diff --git a/ios/include/utils/Zip2Iterator.h b/ios/include/utils/Zip2Iterator.h deleted file mode 100644 index 7b9f552a..00000000 --- a/ios/include/utils/Zip2Iterator.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_ZIP2ITERATOR_H -#define TNT_UTILS_ZIP2ITERATOR_H - -#include -#include - -#include - -namespace utils { - -/* - * A random access iterator that wraps two other random access iterators. - * This mostly exists so that one can sort an array using values from another. - */ - -template -class Zip2Iterator { - std::pair mIt; - using Ref1 = typename std::iterator_traits::reference; - using Ref2 = typename std::iterator_traits::reference; - using Val1 = typename std::iterator_traits::value_type; - using Val2 = typename std::iterator_traits::value_type; - -public: - struct Ref : public std::pair { - using std::pair::pair; - using std::pair::operator=; - private: - friend void swap(Ref lhs, Ref rhs) { - using std::swap; - swap(lhs.first, rhs.first); - swap(lhs.second, rhs.second); - } - }; - - using value_type = std::pair; - using reference = Ref; - using pointer = value_type*; - using difference_type = ptrdiff_t; - using iterator_category = std::random_access_iterator_tag; - - Zip2Iterator() = default; - Zip2Iterator(It1 first, It2 second) : mIt({first, second}) {} - Zip2Iterator(Zip2Iterator const& rhs) noexcept = default; - Zip2Iterator& operator=(Zip2Iterator const& rhs) = default; - - reference operator*() const { return { *mIt.first, *mIt.second }; } - - reference operator[](size_t n) const { return *(*this + n); } - - Zip2Iterator& operator++() { - ++mIt.first; - ++mIt.second; - return *this; - } - - Zip2Iterator& operator--() { - --mIt.first; - --mIt.second; - return *this; - } - - // Postfix operator needed by Microsoft C++ - const Zip2Iterator operator++(int) { - Zip2Iterator t(*this); - mIt.first++; - mIt.second++; - return t; - } - - const Zip2Iterator operator--(int) { - Zip2Iterator t(*this); - mIt.first--; - mIt.second--; - return t; - } - - Zip2Iterator& operator+=(size_t v) { - mIt.first += v; - mIt.second += v; - return *this; - } - - Zip2Iterator& operator-=(size_t v) { - mIt.first -= v; - mIt.second -= v; - return *this; - } - - Zip2Iterator operator+(size_t rhs) const { return { mIt.first + rhs, mIt.second + rhs }; } - Zip2Iterator operator+(size_t rhs) { return { mIt.first + rhs, mIt.second + rhs }; } - Zip2Iterator operator-(size_t rhs) const { return { mIt.first - rhs, mIt.second - rhs }; } - - difference_type operator-(Zip2Iterator const& rhs) const { return mIt.first - rhs.mIt.first; } - - bool operator==(Zip2Iterator const& rhs) const { return (mIt.first == rhs.mIt.first); } - bool operator!=(Zip2Iterator const& rhs) const { return (mIt.first != rhs.mIt.first); } - bool operator>=(Zip2Iterator const& rhs) const { return (mIt.first >= rhs.mIt.first); } - bool operator> (Zip2Iterator const& rhs) const { return (mIt.first > rhs.mIt.first); } - bool operator<=(Zip2Iterator const& rhs) const { return (mIt.first <= rhs.mIt.first); } - bool operator< (Zip2Iterator const& rhs) const { return (mIt.first < rhs.mIt.first); } -}; - -} // namespace utils - -#endif // TNT_UTILS_ZIP2ITERATOR_H diff --git a/ios/include/utils/android/ThermalManager.h b/ios/include/utils/android/ThermalManager.h deleted file mode 100644 index 6c303b04..00000000 --- a/ios/include/utils/android/ThermalManager.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_ANDROID_THERMALMANAGER_H -#define TNT_UTILS_ANDROID_THERMALMANAGER_H - -#include - -#include - -struct AThermalManager; - -namespace utils { - -class ThermalManager { -public: - enum class ThermalStatus : int8_t { - ERROR = -1, - NONE, - LIGHT, - MODERATE, - SEVERE, - CRITICAL, - EMERGENCY, - SHUTDOWN - }; - - ThermalManager(); - ~ThermalManager(); - - // Movable - ThermalManager(ThermalManager&& rhs) noexcept; - ThermalManager& operator=(ThermalManager&& rhs) noexcept; - - // not copiable - ThermalManager(ThermalManager const& rhs) = delete; - ThermalManager& operator=(ThermalManager const& rhs) = delete; - - ThermalStatus getCurrentThermalStatus() const noexcept; - -private: - AThermalManager* mThermalManager = nullptr; -}; - -} // namespace utils - -#endif // TNT_UTILS_ANDROID_THERMALMANAGER_H diff --git a/ios/include/utils/api_level.h b/ios/include/utils/api_level.h deleted file mode 100644 index 0d2ec830..00000000 --- a/ios/include/utils/api_level.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_APILEVEL_H -#define TNT_UTILS_APILEVEL_H - -#include - -namespace utils { - -/** - * Returns this platform's API level. On Android this function will return - * the API level as defined by the SDK API level version. If a platform does - * not have an API level, this function returns 0. - */ -UTILS_PUBLIC -int api_level(); - -} // namespace utils - -#endif // TNT_UTILS_APILEVEL_H diff --git a/ios/include/utils/architecture.h b/ios/include/utils/architecture.h deleted file mode 100644 index 83b11794..00000000 --- a/ios/include/utils/architecture.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_ARCHITECTURE_H -#define TNT_UTILS_ARCHITECTURE_H - -#include - -namespace utils { - -constexpr size_t CACHELINE_SIZE = 64; - -} // namespace utils - -#endif // TNT_UTILS_ARCHITECTURE_H diff --git a/ios/include/utils/ashmem.h b/ios/include/utils/ashmem.h deleted file mode 100644 index c9ca9e3e..00000000 --- a/ios/include/utils/ashmem.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_ASHMEM_H -#define TNT_UTILS_ASHMEM_H - -#include - -namespace utils { - -int ashmem_create_region(const char *name, size_t size); - -} // namespace utils - -#endif // TNT_UTILS_ASHMEM_H diff --git a/ios/include/utils/bitset.h b/ios/include/utils/bitset.h index 45112cec..0d7066e4 100644 --- a/ios/include/utils/bitset.h +++ b/ios/include/utils/bitset.h @@ -25,7 +25,7 @@ #include #include -#include +#include // for std::fill #include #if defined(__ARM_NEON) @@ -314,10 +314,12 @@ private: using bitset8 = bitset; using bitset32 = bitset; +using bitset64 = bitset; using bitset256 = bitset; static_assert(sizeof(bitset8) == sizeof(uint8_t), "bitset8 isn't 8 bits!"); static_assert(sizeof(bitset32) == sizeof(uint32_t), "bitset32 isn't 32 bits!"); +static_assert(sizeof(bitset64) == sizeof(uint64_t), "bitset64 isn't 64 bits!"); } // namespace utils diff --git a/ios/include/utils/compiler.h b/ios/include/utils/compiler.h index 8c3551b1..4b8f5c52 100644 --- a/ios/include/utils/compiler.h +++ b/ios/include/utils/compiler.h @@ -167,6 +167,14 @@ # define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0 #endif +#if defined(_MSC_VER) +// MSVC does not support loop unrolling hints +# define UTILS_NOUNROLL +#else +// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax. +# define UTILS_NOUNROLL _Pragma("nounroll") +#endif + #if __has_feature(cxx_rtti) || defined(_CPPRTTI) # define UTILS_HAS_RTTI 1 #else diff --git a/ios/include/utils/generic/Condition.h b/ios/include/utils/generic/Condition.h deleted file mode 100644 index accde2be..00000000 --- a/ios/include/utils/generic/Condition.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_GENERIC_CONDITION_H -#define TNT_UTILS_GENERIC_CONDITION_H - -#include - -namespace utils { - -class Condition : public std::condition_variable { -public: - using std::condition_variable::condition_variable; - - inline void notify_n(size_t n) noexcept { - if (n == 1) { - notify_one(); - } else if (n > 1) { - notify_all(); - } - } -}; - -} // namespace utils - -#endif // TNT_UTILS_GENERIC_CONDITION_H diff --git a/ios/include/utils/generic/ThermalManager.h b/ios/include/utils/generic/ThermalManager.h deleted file mode 100644 index 2d0088e5..00000000 --- a/ios/include/utils/generic/ThermalManager.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_GENERIC_THERMALMANAGER_H -#define TNT_UTILS_GENERIC_THERMALMANAGER_H - -#include - -#include - -namespace utils { - -class ThermalManager { -public: - enum class ThermalStatus : int8_t { - ERROR = -1, - NONE, - LIGHT, - MODERATE, - SEVERE, - CRITICAL, - EMERGENCY, - SHUTDOWN - }; - - ThermalManager() = default; - - // Movable - ThermalManager(ThermalManager&& rhs) noexcept = default; - ThermalManager& operator=(ThermalManager&& rhs) noexcept = default; - - // not copiable - ThermalManager(ThermalManager const& rhs) = delete; - ThermalManager& operator=(ThermalManager const& rhs) = delete; - - ThermalStatus getCurrentThermalStatus() const noexcept { - return ThermalStatus::NONE; - } -}; - -} // namespace utils - -#endif // TNT_UTILS_GENERIC_THERMALMANAGER_H diff --git a/ios/include/utils/linux/Condition.h b/ios/include/utils/linux/Condition.h deleted file mode 100644 index c2ff21f8..00000000 --- a/ios/include/utils/linux/Condition.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_LINUX_CONDITION_H -#define TNT_UTILS_LINUX_CONDITION_H - -#include -#include -#include // for cv_status -#include -#include // for unique_lock - -#include - -#include - -namespace utils { - -/* - * A very simple condition variable class that can be used as an (almost) drop-in replacement - * for std::condition_variable (doesn't have the timed wait() though). - * It is very low overhead as most of it is inlined. - */ - -class Condition { -public: - Condition() noexcept = default; - Condition(const Condition&) = delete; - Condition& operator=(const Condition&) = delete; - - void notify_all() noexcept { - pulse(std::numeric_limits::max()); - } - - void notify_one() noexcept { - pulse(1); - } - - void notify_n(size_t n) noexcept { - if (n > 0) pulse(n); - } - - void wait(std::unique_lock& lock) noexcept { - wait_until(lock.mutex(), false, nullptr); - } - - template - void wait(std::unique_lock& lock, P predicate) { - while (!predicate()) { - wait(lock); - } - } - - template - std::cv_status wait_until(std::unique_lock& lock, - const std::chrono::time_point& timeout_time) noexcept { - // convert to nanoseconds - uint64_t ns = std::chrono::duration(timeout_time.time_since_epoch()).count(); - using sec_t = decltype(timespec::tv_sec); - using nsec_t = decltype(timespec::tv_nsec); - timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) }; - return wait_until(lock.mutex(), false, &ts); - } - - template - std::cv_status wait_until(std::unique_lock& lock, - const std::chrono::time_point& timeout_time) noexcept { - // convert to nanoseconds - uint64_t ns = std::chrono::duration(timeout_time.time_since_epoch()).count(); - using sec_t = decltype(timespec::tv_sec); - using nsec_t = decltype(timespec::tv_nsec); - timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) }; - return wait_until(lock.mutex(), true, &ts); - } - - template - bool wait_until(std::unique_lock& lock, - const std::chrono::time_point& timeout_time, P predicate) noexcept { - while (!predicate()) { - if (wait_until(lock, timeout_time) == std::cv_status::timeout) { - return predicate(); - } - } - return true; - } - - template - std::cv_status wait_for(std::unique_lock& lock, - const std::chrono::duration& rel_time) noexcept { - return wait_until(lock, std::chrono::steady_clock::now() + rel_time); - } - - template - bool wait_for(std::unique_lock& lock, - const std::chrono::duration& rel_time, P pred) noexcept { - return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred)); - } - -private: - std::atomic mState = { 0 }; - - void pulse(int threadCount) noexcept; - - std::cv_status wait_until(Mutex* lock, - bool realtimeClock, timespec* ts) noexcept; -}; - -} // namespace utils - -#endif // TNT_UTILS_LINUX_CONDITION_H diff --git a/ios/include/utils/linux/Mutex.h b/ios/include/utils/linux/Mutex.h deleted file mode 100644 index 2dcc7ebc..00000000 --- a/ios/include/utils/linux/Mutex.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_LINUX_MUTEX_H -#define TNT_UTILS_LINUX_MUTEX_H - -#include - -#include - -namespace utils { - -/* - * A very simple mutex class that can be used as an (almost) drop-in replacement - * for std::mutex. - * It is very low overhead as most of it is inlined. - */ - -class Mutex { -public: - constexpr Mutex() noexcept = default; - Mutex(const Mutex&) = delete; - Mutex& operator=(const Mutex&) = delete; - - void lock() noexcept { - uint32_t old_state = UNLOCKED; - if (UTILS_UNLIKELY(!mState.compare_exchange_strong(old_state, - LOCKED, std::memory_order_acquire, std::memory_order_relaxed))) { - wait(); - } - } - - void unlock() noexcept { - if (UTILS_UNLIKELY(mState.exchange(UNLOCKED, std::memory_order_release) == LOCKED_CONTENDED)) { - wake(); - } - } - -private: - enum { - UNLOCKED = 0, LOCKED = 1, LOCKED_CONTENDED = 2 - }; - std::atomic mState = { UNLOCKED }; - - void wait() noexcept; - void wake() noexcept; -}; - -} // namespace utils - -#endif // TNT_UTILS_LINUX_MUTEX_H diff --git a/ios/include/utils/ostream.h b/ios/include/utils/ostream.h index 1c5e2602..80358fd4 100644 --- a/ios/include/utils/ostream.h +++ b/ios/include/utils/ostream.h @@ -17,18 +17,21 @@ #ifndef TNT_UTILS_OSTREAM_H #define TNT_UTILS_OSTREAM_H -#include #include #include #include -#include // ssize_t is a POSIX type. +#include +#include namespace utils::io { -class UTILS_PUBLIC ostream { -public: +struct ostream_; +class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { + friend struct ostream_; + +public: virtual ~ostream(); ostream& operator<<(short value) noexcept; @@ -63,6 +66,8 @@ public: ostream& hex() noexcept; protected: + ostream& print(const char* format, ...) noexcept; + class Buffer { public: Buffer() noexcept; @@ -86,11 +91,8 @@ protected: size_t capacity = 0; // total capacity of the buffer }; - std::mutex mLock; - Buffer mData; - Buffer& getBuffer() noexcept { return mData; } - - ostream& print(const char* format, ...) noexcept; + Buffer& getBuffer() noexcept; + Buffer const& getBuffer() const noexcept; private: virtual ostream& flush() noexcept = 0; @@ -105,9 +107,7 @@ private: LONG_DOUBLE }; - inline const char* getFormat(type t) const noexcept; - - bool mShowHex = false; + const char* getFormat(type t) const noexcept; }; // handles std::string diff --git a/ios/include/utils/sstream.h b/ios/include/utils/sstream.h deleted file mode 100644 index 35cb61b6..00000000 --- a/ios/include/utils/sstream.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_SSTREAM_H -#define TNT_UTILS_SSTREAM_H - -#include - -namespace utils { -namespace io { - -class sstream : public ostream { -public: - - ostream &flush() noexcept override; - - const char* c_str() const noexcept; - -}; - -} // namespace io -} // namespace utils - -#endif // TNT_UTILS_SSTREAM_H diff --git a/ios/include/utils/string.h b/ios/include/utils/string.h deleted file mode 100644 index 040044c0..00000000 --- a/ios/include/utils/string.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_STRING_H -#define TNT_UTILS_STRING_H - -#include - -namespace utils { - -float strtof_c(const char* start, char** end); - -} // namespace utils - -#endif // TNT_UTILS_STRING_H diff --git a/ios/include/utils/trap.h b/ios/include/utils/trap.h deleted file mode 100644 index aedc3ddb..00000000 --- a/ios/include/utils/trap.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_TRAP_H -#define TNT_UTILS_TRAP_H - -#include - -#if defined(WIN32) -#include -#include -#endif - -namespace utils { - -// This can be used as a programmatic breakpoint. -inline void debug_trap() noexcept { -#if defined(WIN32) - DebugBreak(); -#else - std::raise(SIGINT); -#endif -} - -} // namespace utils - -#endif // TNT_UTILS_TRAP_H diff --git a/ios/include/utils/vector.h b/ios/include/utils/vector.h deleted file mode 100644 index f02419fe..00000000 --- a/ios/include/utils/vector.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_UTILS_VECTOR_H -#define TNT_UTILS_VECTOR_H - -#include - -namespace utils { - -/** - * Inserts the specified item in the vector at its sorted position. - */ -template -static inline void insert_sorted(std::vector& v, T item) { - auto pos = std::lower_bound(v.begin(), v.end(), item); - v.insert(pos, std::move(item)); -} - -/** - * Inserts the specified item in the vector at its sorted position. - * The item type must implement the < operator. If the specified - * item is already present in the vector, this method returns without - * inserting the item again. - * - * @return True if the item was inserted at is sorted position, false - * if the item already exists in the vector. - */ -template -static inline bool insert_sorted_unique(std::vector& v, T item) { - if (UTILS_LIKELY(v.size() == 0 || v.back() < item)) { - v.push_back(item); - return true; - } - - auto pos = std::lower_bound(v.begin(), v.end(), item); - if (UTILS_LIKELY(pos == v.end() || item < *pos)) { - v.insert(pos, std::move(item)); - return true; - } - - return false; -} - -} // end utils namespace - -#endif // TNT_UTILS_VECTOR_H diff --git a/ios/include/utils/win32/stdtypes.h b/ios/include/utils/win32/stdtypes.h deleted file mode 100644 index f593d8cb..00000000 --- a/ios/include/utils/win32/stdtypes.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -* Copyright (C) 2018 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef TNT_UTILS_WIN32_STDTYPES_H -#define TNT_UTILS_WIN32_STDTYPES_H - -#if defined(WIN32) -#include - -// Copied from linux libc sys/stat.h: -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#define PATH_MAX (MAX_PATH) - -// For getcwd -#include -#define getcwd _getcwd - -#endif -#endif // TNT_UTILS_WIN32_STDTYPES_H diff --git a/ios/include/viewer/AutomationEngine.h b/ios/include/viewer/AutomationEngine.h new file mode 100644 index 00000000..ac8b98f7 --- /dev/null +++ b/ios/include/viewer/AutomationEngine.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_AUTOMATION_ENGINE_H +#define VIEWER_AUTOMATION_ENGINE_H + +#include + +namespace filament { + +class ColorGrading; +class Engine; +class LightManager; +class MaterialInstance; +class Renderer; +class View; + +namespace viewer { + +/** + * The AutomationEngine makes it easy to push a bag of settings values to Filament. + * It can also be used to iterate through settings permutations for testing purposes. + * + * When creating an automation engine for testing purposes, clients give it an immutable reference + * to an AutomationSpec. It is always in one of two states: running or idle. The running state can + * be entered immediately (startRunning) or by requesting batch mode (startBatchMode). + * + * When executing a test, clients should call tick() after each frame is rendered, which gives + * automation an opportunity to push settings to Filament, increment the current test index (if + * enough time has elapsed), and request an asychronous screenshot. + * + * The time to sleep between tests is configurable and can be set to zero. Automation also waits a + * specified minimum number of frames between tests. + * + * Batch mode is meant for non-interactive applications. In batch mode, automation defers applying + * the first test case until the client unblocks it via signalBatchMode(). This is useful when + * waiting for a large model file to become fully loaded. Batch mode also offers a query + * (shouldClose) that is triggered after the last test has been invoked. + */ +class UTILS_PUBLIC AutomationEngine { +public: + /** + * Allows users to toggle screenshots, change the sleep duration between tests, etc. + */ + struct Options { + /** + * Minimum time that automation waits between applying a settings object and advancing + * to the next test case. Specified in seconds. + */ + float sleepDuration = 0.2; + + /** + * Similar to sleepDuration, but expressed as a frame count. Both the minimum sleep time + * and the minimum frame count must be elapsed before automation advances to the next test. + */ + int minFrameCount = 2; + + /** + * If true, test progress is dumped to the utils Log (info priority). + */ + bool verbose = true; + + /** + * If true, the tick function writes out a screenshot before advancing to the next test. + */ + bool exportScreenshots = false; + + /** + * If true, the tick function writes out a settings JSON file before advancing. + */ + bool exportSettings = false; + }; + + /** + * Collection of Filament objects that can be modified by the automation engine. + */ + struct ViewerContent { + View* view; + Renderer* renderer; + MaterialInstance* const* materials; + size_t materialCount; + LightManager* lightManager; + Scene* scene; + IndirectLight* indirectLight; + utils::Entity sunlight; + utils::Entity* assetLights; + size_t assetLightCount; + }; + + /** + * Creates an automation engine and places it in an idle state. + * + * @param spec Specifies a set of settings permutations (owned by the client). + * @param settings Client-owned settings object. This not only supplies the initial + * state, it also receives changes during tick(). This is useful when + * building automation into an application that has a settings UI. + * + * @see setOptions + * @see startRunning + */ + AutomationEngine(const AutomationSpec* spec, Settings* settings) : + mSpec(spec), mSettings(settings) {} + + /** + * Shortcut constructor that creates an automation engine from a JSON string. + * + * This constructor can be used if the user does not need to monitor how the settings + * change over time and does not need ownership over the AutomationSpec. + * + * An example of a JSON spec can be found by searching the repo for DEFAULT_AUTOMATION. + * This is documented using a JSON schema (look for viewer/schemas/automation.json). + * + * @param jsonSpec Valid JSON string that conforms to the automation schema. + * @param size Number of characters in the JSON string. + * @return Automation engine or null if unable to read the JSON. + */ + static AutomationEngine* createFromJSON(const char* jsonSpec, size_t size); + + /** + * Creates an automation engine for the sole purpose of pushing settings, or for executing + * the default test sequence. + * + * To see how the default test sequence is generated, search for DEFAULT_AUTOMATION. + */ + static AutomationEngine* createDefault(); + + /** + * Activates the automation test. During the subsequent call to tick(), the first test is + * applied and automation enters the running state. + */ + void startRunning(); + + /** + * Activates the automation test, but enters a paused state until the user calls + * signalBatchMode(). + */ + void startBatchMode(); + + /** + * Notifies the automation engine that time has passed, a new frame has been rendered. + * + * This is when settings get applied, screenshots are (optionally) exported, and the internal + * test counter is potentially incremented. + * + * @param content Contains the Filament View, Materials, and Renderer that get modified. + * @param deltaTime The amount of time that has passed since the previous tick in seconds. + */ + void tick(const ViewerContent& content, float deltaTime); + + /** + * Mutates a set of client-owned Filament objects according to a JSON string. + * + * This method is an alternative to tick(). It allows clients to use the automation engine as a + * remote control, as opposed to iterating through a predetermined test sequence. + * + * This updates the stashed Settings object, then pushes those settings to the given + * Filament objects. Clients can optionally call getColorGrading() after calling this method. + * + * @param json Contains the JSON string with a set of changes that need to be pushed. + * @param jsonLength Number of characters in the json string. + * @param content Contains a set of Filament objects that you want to mutate. + */ + void applySettings(const char* json, size_t jsonLength, const ViewerContent& content); + + /** + * Gets a color grading object that corresponds to the latest settings. + * + * This method either returns a cached instance, or it destroys the cached instance and creates + * a new one. + */ + ColorGrading* getColorGrading(Engine* engine); + + /** + * Gets the current viewer options. + * + * NOTE: Focal length here might be different from the user-specified value, due to DoF options. + */ + ViewerOptions getViewerOptions() const; + + /** + * Signals that batch mode can begin. Call this after all meshes and textures finish loading. + */ + void signalBatchMode() { mBatchModeAllowed = true; } + + /** + * Cancels an in-progress automation session. + */ + void stopRunning() { mIsRunning = false; } + + /** + * Signals that the application is closing, so all pending screenshots should be cancelled. + */ + void terminate(); + + /** + * Configures the automation engine for users who wish to set up a custom sleep time + * between tests, etc. + */ + void setOptions(Options options) { mOptions = options; } + + /** + * Returns true if automation is in batch mode and all tests have finished. + */ + bool shouldClose() const { return mShouldClose; } + + /** + * Convenience function that writes out a JSON file to disk containing all settings. + * + * @param Settings State vector to serialize. + * @param filename Desired JSON filename. + */ + static void exportSettings(const Settings& settings, const char* filename); + + Options getOptions() const { return mOptions; } + bool isRunning() const { return mIsRunning; } + size_t currentTest() const { return mCurrentTest; } + size_t testCount() const { return mSpec->size(); } + bool isBatchModeEnabled() const { return mBatchModeEnabled; } + const char* getStatusMessage() const; + ~AutomationEngine(); + +private: + AutomationSpec const * const mSpec; + Settings * const mSettings; + Options mOptions; + + Engine* mColorGradingEngine = nullptr; + ColorGrading* mColorGrading = nullptr; + ColorGradingSettings mColorGradingSettings = {}; + + size_t mCurrentTest; + float mElapsedTime; + int mElapsedFrames; + bool mIsRunning = false; + bool mBatchModeEnabled = false; + bool mRequestStart = false; + bool mShouldClose = false; + bool mBatchModeAllowed = false; + bool mTerminated = false; + bool mOwnsSettings = false; + +public: + // For internal use from a screenshot callback. + void requestClose() { mShouldClose = true; } + bool isTerminated() const { return mTerminated; } +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_AUTOMATION_ENGINE_H diff --git a/ios/include/viewer/AutomationSpec.h b/ios/include/viewer/AutomationSpec.h new file mode 100644 index 00000000..49e16854 --- /dev/null +++ b/ios/include/viewer/AutomationSpec.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_AUTOMATION_SPEC_H +#define VIEWER_AUTOMATION_SPEC_H + +#include + +#include + +namespace filament { +namespace viewer { + +/** + * Immutable list of Settings objects generated from a JSON spec. + * + * Each top-level item in the JSON spec is an object with "name", "base" and "permute". + * The "base" object specifies a single set of changes to apply to default settings. + * The optional "permute" object specifies a cross product of changes to apply to the base. + * + * The following example generates a total of 5 test cases. + * [{ + * "name": "simple", + * "base": { + * "view.dof.cocScale": 1.0, + * "view.bloom.strength": 0.5 + * }, + * "permute": { + * "view.bloom.enabled": [false, true], + * "view.dof.enabled": [false, true] + * } + * }, + * { + * "name": "ppoff", + * "base": { + * "view.postProcessingEnabled": false + * } + * }] + */ +class UTILS_PUBLIC AutomationSpec { +public: + + // Parses a JSON spec, then generates a list of Settings objects. + // Returns null on failure (see utils log for warnings and errors). + // Clients should release memory using "delete". + static AutomationSpec* generate(const char* jsonSpec, size_t size); + + // Generates a list of Settings objects using an embedded JSON spec. + static AutomationSpec* generateDefaultTestCases(); + + // Returns the number of generated Settings objects. + size_t size() const; + + // Gets a generated Settings object and copies it out. + // Returns false if the given index is out of bounds. + bool get(size_t index, Settings* out) const; + + // Returns the name of the JSON group for a given Settings object. + char const* getName(size_t index) const; + + // Frees all Settings objects and name strings. + ~AutomationSpec(); + +private: + struct Impl; + AutomationSpec(Impl*); + Impl* mImpl; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_AUTOMATION_SPEC_H diff --git a/ios/include/viewer/RemoteServer.h b/ios/include/viewer/RemoteServer.h new file mode 100644 index 00000000..6272b872 --- /dev/null +++ b/ios/include/viewer/RemoteServer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_REMOTE_SERVER_H +#define VIEWER_REMOTE_SERVER_H + +#include + +#include + +#include +#include + +class CivetServer; + +namespace filament { +namespace viewer { + +class MessageSender; +class MessageReceiver; + +/** + * Encapsulates a message sent from the web client. + * + * All instances of ReceivedMessage and their data / strings are owned by RemoteServer. + * These can be freed via RemoteServer::releaseReceivedMessage(). + */ +struct ReceivedMessage { + char* label; + char* buffer; + size_t bufferByteCount; + size_t messageUid; +}; + +/** + * Manages a tiny WebSocket server that can receive model data and viewer settings. + * + * Client apps can call peekReceivedMessage to check for new data, or acquireReceivedMessage + * to pop it off the small internal queue. When they are done examining the message contents + * they should call releaseReceivedMessage. + */ +class UTILS_PUBLIC RemoteServer { +public: + RemoteServer(int port = 8082); + ~RemoteServer(); + bool isValid() const { return mMessageSender; } + + /** + * Checks if a download is currently in progress and returns its label. + * Returns null if nothing is being downloaded. + */ + char const* peekIncomingLabel() const; + + /** + * Pops a message off the incoming queue or returns null if there are no unread messages. + * + * After examining its contents, users should free the message with releaseReceivedMessage. + */ + ReceivedMessage const* acquireReceivedMessage(); + + /** + * Frees the memory that holds the contents of a received message. + */ + void releaseReceivedMessage(ReceivedMessage const* message); + + void sendMessage(const Settings& settings); + void sendMessage(const char* label, const char* buffer, size_t bufsize); + + // For internal use (makes JNI simpler) + ReceivedMessage const* peekReceivedMessage() const; + +private: + void enqueueReceivedMessage(ReceivedMessage* message); + void setIncomingMessage(ReceivedMessage* message); + MessageSender* mMessageSender = nullptr; + MessageReceiver* mMessageReceiver = nullptr; + size_t mNextMessageUid = 0; + static const size_t kMessageCapacity = 4; + ReceivedMessage* mReceivedMessages[kMessageCapacity] = {}; + ReceivedMessage* mIncomingMessage = nullptr; + JsonSerializer mSerializer; + mutable std::mutex mReceivedMessagesMutex; + friend class MessageReceiver; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_REMOTE_SERVER_H diff --git a/ios/include/viewer/Settings.h b/ios/include/viewer/Settings.h new file mode 100644 index 00000000..248553f1 --- /dev/null +++ b/ios/include/viewer/Settings.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_SETTINGS_H +#define VIEWER_SETTINGS_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace filament { + +class Skybox; +class Renderer; + +namespace viewer { + +struct ColorGradingSettings; +struct DynamicLightingSettings; +struct MaterialSettings; +struct Settings; +struct ViewSettings; +struct LightSettings; +struct ViewerOptions; + +enum class ToneMapping : uint8_t { + LINEAR = 0, + ACES_LEGACY = 1, + ACES = 2, + FILMIC = 3, + GENERIC = 4, + DISPLAY_RANGE = 5, +}; + +using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions; +using ScreenSpaceReflectionsOptions = filament::View::ScreenSpaceReflectionsOptions; +using AntiAliasing = filament::View::AntiAliasing; +using BloomOptions = filament::View::BloomOptions; +using DepthOfFieldOptions = filament::View::DepthOfFieldOptions; +using Dithering = filament::View::Dithering; +using FogOptions = filament::View::FogOptions; +using RenderQuality = filament::View::RenderQuality; +using ShadowType = filament::View::ShadowType; +using DynamicResolutionOptions = filament::View::DynamicResolutionOptions; +using MultiSampleAntiAliasingOptions = filament::View::MultiSampleAntiAliasingOptions; +using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions; +using VignetteOptions = filament::View::VignetteOptions; +using VsmShadowOptions = filament::View::VsmShadowOptions; +using LightManager = filament::LightManager; + +// These functions push all editable property values to their respective Filament objects. +void applySettings(const ViewSettings& settings, View* dest); +void applySettings(const MaterialSettings& settings, MaterialInstance* dest); +void applySettings(const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight, + utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view); +void applySettings(const ViewerOptions& settings, Camera* camera, Skybox* skybox, + Renderer* renderer); + +// Creates a new ColorGrading object based on the given settings. +UTILS_PUBLIC +ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine); + +class UTILS_PUBLIC JsonSerializer { +public: + JsonSerializer(); + ~JsonSerializer(); + + // Writes a human-readable JSON string into an internal buffer and returns the result. + const std::string& writeJson(const Settings& in); + + // Reads the given JSON blob and updates the corresponding fields in the given Settings object. + // - The given JSON blob need not specify all settings. + // - Returns true if successful. + // - This function writes warnings and error messages into the utils log. + bool readJson(const char* jsonChunk, size_t size, Settings* out); + +private: + class Context; + Context* context; +}; + +struct GenericToneMapperSettings { + float contrast = 1.55f; + float midGrayIn = 0.18f; + float midGrayOut = 0.215f; + float hdrMax = 10.0f; + bool operator!=(const GenericToneMapperSettings &rhs) const { return !(rhs == *this); } + bool operator==(const GenericToneMapperSettings &rhs) const; +}; + +struct ColorGradingSettings { + bool enabled = true; + filament::ColorGrading::QualityLevel quality = filament::ColorGrading::QualityLevel::MEDIUM; + ToneMapping toneMapping = ToneMapping::ACES_LEGACY; + GenericToneMapperSettings genericToneMapper; + bool luminanceScaling = false; + bool gamutMapping = false; + float exposure = 0.0f; + float nightAdaptation = 0.0f; + float temperature = 0.0f; + float tint = 0.0f; + math::float3 outRed{1.0f, 0.0f, 0.0f}; + math::float3 outGreen{0.0f, 1.0f, 0.0f}; + math::float3 outBlue{0.0f, 0.0f, 1.0f}; + math::float4 shadows{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 midtones{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 highlights{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 ranges{0.0f, 0.333f, 0.550f, 1.0f}; + float contrast = 1.0f; + float vibrance = 1.0f; + float saturation = 1.0f; + math::float3 slope{1.0f}; + math::float3 offset{0.0f}; + math::float3 power{1.0f}; + math::float3 gamma{1.0f}; + math::float3 midPoint{1.0f}; + math::float3 scale{1.0f}; + bool linkedCurves = false; + bool operator!=(const ColorGradingSettings &rhs) const { return !(rhs == *this); } + bool operator==(const ColorGradingSettings &rhs) const; +}; + +struct DynamicLightingSettings { + float zLightNear = 5; + float zLightFar = 100; +}; + +// This defines fields in the same order as the setter methods in filament::View. +struct ViewSettings { + // standalone View settings + AntiAliasing antiAliasing = AntiAliasing::FXAA; + Dithering dithering = Dithering::TEMPORAL; + ShadowType shadowType = ShadowType::PCF; + bool postProcessingEnabled = true; + + // View Options (sorted) + AmbientOcclusionOptions ssao; + ScreenSpaceReflectionsOptions screenSpaceReflections; + BloomOptions bloom; + DepthOfFieldOptions dof; + DynamicResolutionOptions dsr; + FogOptions fog; + MultiSampleAntiAliasingOptions msaa; + RenderQuality renderQuality; + TemporalAntiAliasingOptions taa; + VignetteOptions vignette; + VsmShadowOptions vsmShadowOptions; + + // Custom View Options + ColorGradingSettings colorGrading; + DynamicLightingSettings dynamicLighting; +}; + +template +struct MaterialProperty { std::string name; T value; }; + +// This struct has a fixed size for simplicity. Each non-empty property name is an override. +struct MaterialSettings { + static constexpr size_t MAX_COUNT = 4; + MaterialProperty scalar[MAX_COUNT]; + MaterialProperty float3[MAX_COUNT]; + MaterialProperty float4[MAX_COUNT]; +}; + +struct LightSettings { + bool enableShadows = true; + bool enableSunlight = true; + LightManager::ShadowOptions shadowOptions; + SoftShadowOptions softShadowOptions; + float sunlightIntensity = 100000.0f; + math::float3 sunlightDirection = {0.6, -1.0, -0.8};; + math::float3 sunlightColor = filament::Color::toLinear({ 0.98, 0.92, 0.89}); + float iblIntensity = 30000.0f; + float iblRotation = 0.0f; +}; + +struct ViewerOptions { + float cameraAperture = 16.0f; + float cameraSpeed = 125.0f; + float cameraISO = 100.0f; + float groundShadowStrength = 0.75f; + bool groundPlaneEnabled = false; + bool skyboxEnabled = true; + sRGBColor backgroundColor = { 0.0f }; + float cameraFocalLength = 28.0f; + float cameraFocusDistance = 10.0f; + bool autoScaleEnabled = true; +}; + +struct Settings { + ViewSettings view; + MaterialSettings material; + LightSettings lighting; + ViewerOptions viewer; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_SETTINGS_H diff --git a/ios/include/viewer/SimpleViewer.h b/ios/include/viewer/SimpleViewer.h new file mode 100644 index 00000000..24cae0c2 --- /dev/null +++ b/ios/include/viewer/SimpleViewer.h @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_SIMPLEVIEWER_H +#define VIEWER_SIMPLEVIEWER_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace filagui { + class ImGuiHelper; +} + +namespace filament { +namespace viewer { + +/** + * \class SimpleViewer SimpleViewer.h viewer/SimpleViewer.h + * \brief Manages the state for a simple glTF viewer with imgui controls and a tree view. + * + * This is a utility that can be used across multiple platforms, including web. + * + * \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader + * instead. + */ +class UTILS_PUBLIC SimpleViewer { +public: + using Animator = gltfio::Animator; + using FilamentAsset = gltfio::FilamentAsset; + using FilamentInstance = gltfio::FilamentInstance; + + static constexpr int DEFAULT_SIDEBAR_WIDTH = 350; + + /** + * Constructs a SimpleViewer that has a fixed association with the given Filament objects. + * + * Upon construction, the simple viewer may create some additional Filament objects (such as + * light sources) that it owns. + */ + SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view, + int sidebarWidth = DEFAULT_SIDEBAR_WIDTH); + + /** + * Destroys the SimpleViewer and any Filament entities that it owns. + */ + ~SimpleViewer(); + + /** + * Adds the asset's ready-to-render entities into the scene. + * + * The viewer does not claim ownership over the asset or its entities. Clients should use + * AssetLoader and ResourceLoader to load an asset before passing it in. + * + * @param asset The asset to view. + * @param instanceToAnimate Optional instance from which to get the animator. + */ + void populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate = nullptr); + + /** + * Removes the current asset from the viewer. + * + * This removes all the asset entities from the Scene, but does not destroy them. + */ + void removeAsset(); + + /** + * Sets or changes the current scene's IBL to allow the UI manipulate it. + */ + void setIndirectLight(filament::IndirectLight* ibl, filament::math::float3 const* sh3); + + /** + * Applies the currently-selected glTF animation to the transformation hierarchy and updates + * the bone matrices on all renderables. + */ + void applyAnimation(double currentTime); + + /** + * Constructs ImGui controls for the current frame and responds to everything that the user has + * changed since the previous frame. + * + * If desired this can be used in conjunction with the filagui library, which allows clients to + * render ImGui controls with Filament. + */ + void updateUserInterface(); + + /** + * Alternative to updateUserInterface that uses an internal instance of ImGuiHelper. + * + * This utility method is designed for clients that do not want to manage their own instance of + * ImGuiHelper (e.g., JavaScript clients). + * + * Behind the scenes this simply calls ImGuiHelper->render() and passes updateUserInterface into + * its callback. Note that the first call might be slower since it requires the creation of the + * internal ImGuiHelper instance. + */ + void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio); + + /** + * Event-passing methods, useful only when SimpleViewer manages its own instance of ImGuiHelper. + * The key codes used in these methods are just normal ASCII/ANSI codes. + * @{ + */ + void mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY, bool control); + void keyDownEvent(int keyCode); + void keyUpEvent(int keyCode); + void keyPressEvent(int charCode); + /** @}*/ + + /** + * Retrieves the current width of the ImGui "window" which we are using as a sidebar. + * Clients can monitor this value to adjust the size of the view. + */ + int getSidebarWidth() const { return mSidebarWidth; } + + /** + * Allows clients to inject custom UI. + */ + void setUiCallback(std::function callback) { mCustomUI = callback; } + + /** + * Draws the bounding box of each renderable. + * Defaults to false. + */ + void enableWireframe(bool b) { mEnableWireframe = b; } + + /** + * Enables a built-in light source (useful for creating shadows). + * Defaults to true. + */ + void enableSunlight(bool b) { mSettings.lighting.enableSunlight = b; } + + /** + * Enables dithering on the view. + * Defaults to true. + */ + void enableDithering(bool b) { + mSettings.view.dithering = b ? Dithering::TEMPORAL : Dithering::NONE; + } + + /** + * Enables FXAA antialiasing in the post-process pipeline. + * Defaults to true. + */ + void enableFxaa(bool b) { + mSettings.view.antiAliasing = b ? AntiAliasing::FXAA : AntiAliasing::NONE; + } + + /** + * Enables hardware-based MSAA antialiasing. + * Defaults to true. + */ + void enableMsaa(bool b) { + mSettings.view.msaa.sampleCount = 4; + mSettings.view.msaa.enabled = b; + } + + /** + * Enables screen-space ambient occlusion in the post-process pipeline. + * Defaults to true. + */ + void enableSSAO(bool b) { mSettings.view.ssao.enabled = b; } + + /** + * Enables Bloom. + * Defaults to true. + */ + void enableBloom(bool bloom) { mSettings.view.bloom.enabled = bloom; } + + /** + * Adjusts the intensity of the IBL. + * See also filament::IndirectLight::setIntensity(). + * Defaults to 30000.0. + */ + void setIBLIntensity(float brightness) { mSettings.lighting.iblIntensity = brightness; } + + /** + * Updates the transform at the root node according to the autoScaleEnabled setting. + */ + void updateRootTransform(); + + /** + * Gets a modifiable reference to stashed state. + */ + Settings& getSettings() { return mSettings; } + + void stopAnimation() { mCurrentAnimation = 0; } + + int getCurrentCamera() const { return mCurrentCamera; } + +private: + void updateIndirectLight(); + + // Immutable properties set from the constructor. + filament::Engine* const mEngine; + filament::Scene* const mScene; + filament::View* const mView; + const utils::Entity mSunlight; + + // Lazily instantiated fields. + filagui::ImGuiHelper* mImGuiHelper = nullptr; + + // Properties that can be changed from the application. + FilamentAsset* mAsset = nullptr; + Animator* mAnimator = nullptr; + filament::IndirectLight* mIndirectLight = nullptr; + std::function mCustomUI; + + // Properties that can be changed from the UI. + int mCurrentAnimation = 1; // It is a 1-based index and 0 means not playing animation + int mCurrentVariant = 0; + bool mResetAnimation = true; + bool mEnableWireframe = false; + int mVsmMsaaSamplesLog2 = 1; + Settings mSettings; + int mSidebarWidth; + uint32_t mFlags; + utils::Entity mCurrentMorphingEntity; + std::vector mMorphWeights; + + // 0 is the default "free camera". Additional cameras come from the gltf file (1-based index). + int mCurrentCamera = 0; + + // Color grading UI state. + float mToneMapPlot[1024]; + float mRangePlot[1024 * 3]; + float mCurvePlot[1024 * 3]; +}; + +UTILS_PUBLIC +filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffset); + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_SIMPLEVIEWER_H diff --git a/ios/lib/libOGLCompiler.a b/ios/lib/libOGLCompiler.a deleted file mode 100644 index 3a27c5bb..00000000 --- a/ios/lib/libOGLCompiler.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2faa3c1716a1da8bbd4ffb10fdf24162ce1e7ba78be0f7f75ec6041133b61b31 -size 7912 diff --git a/ios/lib/libOSDependent.a b/ios/lib/libOSDependent.a deleted file mode 100644 index b77ee3f0..00000000 --- a/ios/lib/libOSDependent.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b3ef274d0f92ac77e028ffcbe0dffcd9b20b44e71b01be9edc387e4bc6d4675b -size 15224 diff --git a/ios/lib/libSPIRV-Tools-link.a b/ios/lib/libSPIRV-Tools-link.a deleted file mode 100644 index ff799928..00000000 --- a/ios/lib/libSPIRV-Tools-link.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0427cdda89024752dce8bb59be7da81abe28677a23e3aba510deea40bb6e58df -size 7209912 diff --git a/ios/lib/libSPIRV-Tools-opt.a b/ios/lib/libSPIRV-Tools-opt.a deleted file mode 100644 index d5c08e70..00000000 --- a/ios/lib/libSPIRV-Tools-opt.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:212f81879691d1fe205e5e97c81e4ebbfb367d684b5349acf36c4b9d7f48a3e4 -size 566729104 diff --git a/ios/lib/libSPIRV-Tools-reduce.a b/ios/lib/libSPIRV-Tools-reduce.a deleted file mode 100644 index 9a6eb890..00000000 --- a/ios/lib/libSPIRV-Tools-reduce.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:16db7dedd5e6e143c4b28228c345222a6b478f6d5f416989dc1167405faf1a61 -size 138091336 diff --git a/ios/lib/libSPIRV-Tools.a b/ios/lib/libSPIRV-Tools.a deleted file mode 100644 index e694d6ec..00000000 --- a/ios/lib/libSPIRV-Tools.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ce4e323da2c8ecd3715d9e2451b4f70e0f99f69d1ef6c73f4b207c117c15a01 -size 119018600 diff --git a/ios/lib/libSPIRV.a b/ios/lib/libSPIRV.a deleted file mode 100644 index ddc1e9d8..00000000 --- a/ios/lib/libSPIRV.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:44ebf36681bc0dbac2f3ad88ccbe165a99a87a761bc163769519c95bed53121b -size 12947112 diff --git a/ios/lib/libSPVRemapper.a b/ios/lib/libSPVRemapper.a deleted file mode 100644 index c747e182..00000000 --- a/ios/lib/libSPVRemapper.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a581e9901cfbb0b0cc54b7c9e463e1858f3b8cee727b430fba64f6be0c479ca -size 5517256 diff --git a/ios/lib/libbackend.a b/ios/lib/libbackend.a index 21f65844..9322d844 100644 --- a/ios/lib/libbackend.a +++ b/ios/lib/libbackend.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11e90e7d9e6d6915d8ca4cb060f43872791e23dadf48cf44baf5b03bb4fef9bc -size 2304656 +oid sha256:7e1c8cf72f9ab44b73a925270488268424c13ebb5be5068e434e9b9b42835fbc +size 4714552 diff --git a/ios/lib/libbackend_test.a b/ios/lib/libbackend_test.a deleted file mode 100644 index 8a8a4049..00000000 --- a/ios/lib/libbackend_test.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea46b8236ae58c2c6bf00de5d2901fd6ba8a3521b4ab4222c915e2256d271d36 -size 11289856 diff --git a/ios/lib/libbenchmark.a b/ios/lib/libbenchmark.a deleted file mode 100644 index 970b925f..00000000 --- a/ios/lib/libbenchmark.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1d7b64227a24fa5fa7d82a7bc38f9dc7d1780010711be72b0c95b5c8ba4129e -size 7854928 diff --git a/ios/lib/libbenchmark_main.a b/ios/lib/libbenchmark_main.a deleted file mode 100644 index 1c383bdd..00000000 --- a/ios/lib/libbenchmark_main.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2db8303ca029a06610f1ef5c8266ba4e473a24f9b235a5ca61b0c6f8d1c1d7f6 -size 20160 diff --git a/ios/lib/libcamutils.a b/ios/lib/libcamutils.a index a4926d42..31993122 100644 --- a/ios/lib/libcamutils.a +++ b/ios/lib/libcamutils.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20c63c31da38a6e04d490517e36ecb3706de0c072069c91888f68657cbadbc44 -size 80736 +oid sha256:fbeee9b9839c36d7683120204026e064b814b9780f0b52bd254bb6ae29a689e0 +size 169336 diff --git a/ios/lib/libcivetweb.a b/ios/lib/libcivetweb.a index 7813f7ff..8f81f0cc 100644 --- a/ios/lib/libcivetweb.a +++ b/ios/lib/libcivetweb.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69fc99b7a71f89d86cb1b616264ac49ca8a861789e735d8a0cf4b5980d61b0f2 -size 613712 +oid sha256:dfeef92c7ccef5888970690b120254931586e32fcd75b31604e14061a1b59a41 +size 1187768 diff --git a/ios/lib/libdracodec.a b/ios/lib/libdracodec.a index 6fedc2c2..97e5c881 100644 --- a/ios/lib/libdracodec.a +++ b/ios/lib/libdracodec.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b181494750fc50d2a161e961d8ccdb72c931258da98a7ad807401134b37b4fe0 -size 3916120 +oid sha256:dbe6bb36e793da6ae4bf3c882394ca71a0aaf0217a1758b516b19db462864171 +size 8307784 diff --git a/ios/lib/libfilabridge.a b/ios/lib/libfilabridge.a index 28bcd1e2..531ecde3 100644 --- a/ios/lib/libfilabridge.a +++ b/ios/lib/libfilabridge.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc40c9d0a555ec154f2c359df4eb0af2bacffe1dcad1fe2db38625d59e0ccb4e -size 177992 +oid sha256:458703c15800b71797665a3971d94dc0fd80c0a70d61c7101a499a7a3612d00a +size 261592 diff --git a/ios/lib/libfilaflat.a b/ios/lib/libfilaflat.a index c3510e80..b236f44c 100644 --- a/ios/lib/libfilaflat.a +++ b/ios/lib/libfilaflat.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:196f0b001ba0c408d09c44ce34001c5d310b6b2026965d8cbcc53dda22ce00a0 -size 109872 +oid sha256:e0547af8b832b16f4b99b95f47a49ec37a6eaac04acc06aa10c428ce7fd8586d +size 219496 diff --git a/ios/lib/libfilagui.a b/ios/lib/libfilagui.a deleted file mode 100644 index 03c3b6c2..00000000 --- a/ios/lib/libfilagui.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5294fe6de674bdf10f2528a040f78988fc1d7d6c3eb76a1b180b232f26f6f4e1 -size 935608 diff --git a/ios/lib/libfilamat.a b/ios/lib/libfilamat.a index 6a947746..1265a7a6 100644 --- a/ios/lib/libfilamat.a +++ b/ios/lib/libfilamat.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61f1940eefdda9723f55ff46839c85e76932a5a7499f130ef7265cd3c1f3c9de -size 50209608 +oid sha256:6672698f5dab43aa40c987df931bea50d5123c807160562e2099fd3e33593c56 +size 103312520 diff --git a/ios/lib/libfilamat_combined.a b/ios/lib/libfilamat_combined.a deleted file mode 100644 index 68957318..00000000 --- a/ios/lib/libfilamat_combined.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c18ca477be3ae5f806bad5d96393a7396f82d8826058afc6c3dcf2f3c49f200 -size 822373504 diff --git a/ios/lib/libfilamat_lite.a b/ios/lib/libfilamat_lite.a index 14f0b166..163e9c33 100644 --- a/ios/lib/libfilamat_lite.a +++ b/ios/lib/libfilamat_lite.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc5514d3c056cf668dc531675df8f9c57a5330a80c039c52de273bd3d955e275 -size 897496 +oid sha256:1340c5440d6b5d21ffcb91f621aa9400613ae2f72a9dcac3e4dcd1b794d31eae +size 1797264 diff --git a/ios/lib/libfilament-iblprefilter.a b/ios/lib/libfilament-iblprefilter.a index f20904c3..326a8e99 100644 --- a/ios/lib/libfilament-iblprefilter.a +++ b/ios/lib/libfilament-iblprefilter.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68547b1a33f82052a917b6fe64d8ef95b0c07c700143c1b61a5064f8ee135ef3 -size 92568 +oid sha256:602177b7cdf830450cdbadb401c900029db5387ba72bb69b402d5c66c7c63928 +size 193024 diff --git a/ios/lib/libfilament.a b/ios/lib/libfilament.a index c5967802..6cb6d0a5 100644 --- a/ios/lib/libfilament.a +++ b/ios/lib/libfilament.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a838f3161ee9449f1ef4168344c73f4e5db15b3ba2f45789a5a8fcdac10e8576 -size 4397248 +oid sha256:4e76da6e9ed31fa1ccaf2ad8180492a4037c6a74f8d5ba7c4e1e86c8fc80f604 +size 10364960 diff --git a/ios/lib/libfilameshio.a b/ios/lib/libfilameshio.a index 6f301c69..fd646c3e 100644 --- a/ios/lib/libfilameshio.a +++ b/ios/lib/libfilameshio.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d10e2a4528c75118e65c29660ef3104a658a1cd493e3a8cf792f422986941316 -size 70552 +oid sha256:6746bf8b4fc977dde401b49d37ce96098ec46929b982eac6e8f9ef5567fce640 +size 144040 diff --git a/ios/lib/libgeometry.a b/ios/lib/libgeometry.a index 5e22224b..b8abee5c 100644 --- a/ios/lib/libgeometry.a +++ b/ios/lib/libgeometry.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08ffcafebe8770c4a0b9bd5e44138c8e688138d4524a46e08d90be5e37cb7b29 -size 63680 +oid sha256:97e71390fe53d07abfa7ea2d33a459aeff003029bbb47951727ba9f6be1bcd1a +size 150448 diff --git a/ios/lib/libgetopt.a b/ios/lib/libgetopt.a deleted file mode 100644 index 4aa09dd0..00000000 --- a/ios/lib/libgetopt.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:87a223d8302b218b1a1d4c6c9640c4f3e2fb4475694d9552763b7bdd865eee54 -size 17024 diff --git a/ios/lib/libglslang.a b/ios/lib/libglslang.a deleted file mode 100644 index d5a34471..00000000 --- a/ios/lib/libglslang.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f185187cb100b8a6a5b6bfb3b1ca9050cc401dcd19c2fe1371eadbc6ee2775dc -size 64417576 diff --git a/ios/lib/libgltfio_core.a b/ios/lib/libgltfio_core.a index 33c06ab9..18a6cb40 100644 --- a/ios/lib/libgltfio_core.a +++ b/ios/lib/libgltfio_core.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7bdbcc624e1c579ce5012056c985baee50d88cd1cf4c92b3b6b8a0bbc7e34aa -size 2125536 +oid sha256:cf6810e07e669db75c26300a445178bd6cbef71aeacb3f6d1d54ee1f3eec83b2 +size 4351144 diff --git a/ios/lib/libgltfio_resources.a b/ios/lib/libgltfio_resources.a index db2f4c00..f66d6e74 100644 --- a/ios/lib/libgltfio_resources.a +++ b/ios/lib/libgltfio_resources.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84fb1fe335b227e11f22b25eb0ad325414f916e04f4ef8a52395aac1b5e29f07 -size 6302744 +oid sha256:1f9af1afc57a55239f905fc93fac37505a5f3d4563ddd7d6388c6443e05e51f7 +size 16508792 diff --git a/ios/lib/libgltfio_resources_lite.a b/ios/lib/libgltfio_resources_lite.a index b9b38276..49742d84 100644 --- a/ios/lib/libgltfio_resources_lite.a +++ b/ios/lib/libgltfio_resources_lite.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49931f747ee8f3bab1808c654836ac4b0a9ab541b1c4c3df957208a2969837d1 -size 1171208 +oid sha256:3a2c0abf0d58f049697c124ceb8f6cb426422a58dff5d917a026f6a45069bb26 +size 3149464 diff --git a/ios/lib/libgtest.a b/ios/lib/libgtest.a deleted file mode 100644 index 269fd33f..00000000 --- a/ios/lib/libgtest.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c0143ebd1104445d81a5ad67a006dfae1be32482f6b0a9853134df00357bbee -size 2890608 diff --git a/ios/lib/libibl-lite.a b/ios/lib/libibl-lite.a index d2449a50..fd2d71b0 100644 --- a/ios/lib/libibl-lite.a +++ b/ios/lib/libibl-lite.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ea467954333f7b541663e7efc1377a0fe2170641035486baa5eb8bb220d0571 -size 467736 +oid sha256:ec4834e669ea81d30ac0ec6d0c552a00bbde477934e89b2f1cdd4cea8771b44f +size 974720 diff --git a/ios/lib/libibl.a b/ios/lib/libibl.a index d8ce9540..6429123d 100644 --- a/ios/lib/libibl.a +++ b/ios/lib/libibl.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab3234fd2f23a339792e295ea4bc52e03365aa3b77360a8be76ecfbc66a5a7a3 -size 563936 +oid sha256:3d5b89dd8f4350909274e10c7c013fa659a12b0f7d25710b576f11208c62a6b7 +size 1175416 diff --git a/ios/lib/libimage.a b/ios/lib/libimage.a index 56de4b7c..d5f7cbf5 100644 --- a/ios/lib/libimage.a +++ b/ios/lib/libimage.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:425e24fc0a5b898be11df0649e105f7d806aad993e7eda6ca1ef8cb5623ef751 -size 171288 +oid sha256:db9243678a9df0d4ba37346132faa3224814481e8364c7a07a925d9198b33545 +size 357528 diff --git a/ios/lib/libimgui.a b/ios/lib/libimgui.a deleted file mode 100644 index 29e88157..00000000 --- a/ios/lib/libimgui.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2c05493099298be585a61e97d603ea7189380235d39042de4f61cd5687ed9e5 -size 2633392 diff --git a/ios/lib/libmath.a b/ios/lib/libmath.a deleted file mode 100644 index 09c7fa6a..00000000 --- a/ios/lib/libmath.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:503aa5b3f0ac1eb3e781acc387b303ddc15602e9a0d24bdb308b0aa25a8d373b -size 1264 diff --git a/ios/lib/libmathio.a b/ios/lib/libmathio.a deleted file mode 100644 index c7a6365e..00000000 --- a/ios/lib/libmathio.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a13205178065ba482ba932444bc319cd155086b75e6b0481dfcc59dcc9798b5d -size 222568 diff --git a/ios/lib/libmeshoptimizer.a b/ios/lib/libmeshoptimizer.a index b81e8329..99e34df6 100644 --- a/ios/lib/libmeshoptimizer.a +++ b/ios/lib/libmeshoptimizer.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8281835873be7620013024879f76498420248f738e0f05c33cdbac639f75240 -size 160632 +oid sha256:b1374efa1ab6c70e7cd3b0c6c5d0e4ff10dcc6d0f59526f8488a2e31976fb0b3 +size 351400 diff --git a/ios/lib/libshaders.a b/ios/lib/libshaders.a index 43db4bab..16ee60fa 100644 --- a/ios/lib/libshaders.a +++ b/ios/lib/libshaders.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4385bc2ef9bb3b0b3087b0a6f3bb8b9b82b3fd8fdcf06129f3dc330a5de2ec07 -size 108768 +oid sha256:0f85c22ea7b2f5bd28de495ad1c3ee538a05076e742c8d883d40e30c2a03914d +size 240856 diff --git a/ios/lib/libsmol-v.a b/ios/lib/libsmol-v.a index 7357771d..3f4aad7b 100644 --- a/ios/lib/libsmol-v.a +++ b/ios/lib/libsmol-v.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d9f709c0bd355fb2ba1e0af4ec11c5ad99ccb3a84361346565360730f00d9f2 -size 120696 +oid sha256:1668a0bee6ca4a0a72c5c497361c866d90d2a42d4df1cafd3ab29d64aa4bf4bb +size 237152 diff --git a/ios/lib/libspirv-cross-core.a b/ios/lib/libspirv-cross-core.a deleted file mode 100644 index 9868ed6e..00000000 --- a/ios/lib/libspirv-cross-core.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3fb733790c862b884cc66af0388ad448e2f7b21abe512001b060619de8207b39 -size 8709984 diff --git a/ios/lib/libspirv-cross-glsl.a b/ios/lib/libspirv-cross-glsl.a deleted file mode 100644 index 10bf69f2..00000000 --- a/ios/lib/libspirv-cross-glsl.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f16ac51ef9e7c77a5b4f53c3eb84c4c7f73c6aa7ab0b43d042428b3fb784e59d -size 6573200 diff --git a/ios/lib/libspirv-cross-msl.a b/ios/lib/libspirv-cross-msl.a deleted file mode 100644 index e704e9d2..00000000 --- a/ios/lib/libspirv-cross-msl.a +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e845a3fcafa2a82684d35449aa2634a546496f89f9789dd62a6d9d295e95afb -size 18418288 diff --git a/ios/lib/libutils.a b/ios/lib/libutils.a index 773d9a85..aadf5102 100644 --- a/ios/lib/libutils.a +++ b/ios/lib/libutils.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c108ddb221f3aec50e3fb32cd7c03d37684f047e8e2515fd9a0575c5f643d94a -size 485496 +oid sha256:040826e98333438a7853894853467f592714deef7d8c0405bda00ca2b00390c0 +size 1046760 diff --git a/ios/lib/libviewer.a b/ios/lib/libviewer.a index 808f99e7..c8e1f50a 100644 --- a/ios/lib/libviewer.a +++ b/ios/lib/libviewer.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1916b3c12e0fae9384bf6a5d119c8bd063153eaddcabf5fa8bbd0b686b24977 -size 766952 +oid sha256:f65eaf66093e2c4b81ea0bf9dc976343e6156afb95d67bee9b0013e2b7a1a2c8 +size 1602256 diff --git a/ios/lib/libvkshaders.a b/ios/lib/libvkshaders.a index 98699f8b..1b6cf1ab 100644 --- a/ios/lib/libvkshaders.a +++ b/ios/lib/libvkshaders.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f501302f68ea602952be75633a38ade3c7254ab0d755b5344635f5aaa56a856 -size 4320 +oid sha256:a4a97e1230674f2f3ca07612b5bba71d348edb38b5293ad4f340989749f2ecfc +size 8832 diff --git a/ios/polyvox_filament.podspec b/ios/polyvox_filament.podspec index 52f09b87..75232ead 100644 --- a/ios/polyvox_filament.podspec +++ b/ios/polyvox_filament.podspec @@ -21,6 +21,9 @@ A new flutter plugin project. s.static_framework = true s.vendored_libraries = "lib/*.a" + s.library = "c++" + + # Flutter.framework does not contain a i386 slice. s.user_target_xcconfig = { 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', diff --git a/ios/src/DependencyGraph.h b/ios/src/DependencyGraph.h deleted file mode 100644 index 854571c2..00000000 --- a/ios/src/DependencyGraph.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_DEPENDENCY_GRAPH_H -#define GLTFIO_DEPENDENCY_GRAPH_H - -#include - -#include -#include - -#include -#include - -namespace filament { - class MaterialInstance; - class Texture; -} - -namespace gltfio { - -/** - * Internal graph that enables FilamentAsset to discover "ready-to-render" entities by tracking - * the loading status of Texture objects that each entity depends on. - * - * Renderables connect to a set of material instances, which in turn connect to a set of parameter - * names, which in turn connect to a set of texture objects. These relationships are not easily - * inspectable using the Filament API or ECS. - * - * One graph corresponds to a single glTF asset. The graph only contains weak references, it does - * not have ownership over any Filament objects. Here's an example: - * - * Entity Entity Entity Entity - * | / \ | / - * | / \ | / - * Material Material Material - * / | \ | - * / | \ | - * Param Param Param Param - * \ / | | - * \ / | | - * Texture Texture Texture - * - * Note that the left-most entity in the above graph has no textures, so it becomes ready as soon as - * finalize is called. - */ -class DependencyGraph { -public: - using Material = filament::MaterialInstance; - using Entity = utils::Entity; - - // Pops up to "count" ready-to-render entities off the queue. - // If "result" is non-null, returns the number of written items. - // If "result" is null, returns the number of available entities. - size_t popRenderables(Entity* result, size_t count) noexcept; - - // These are called during the initial asset loader phase. - void addEdge(Entity entity, Material* material); - void addEdge(Material* material, const char* parameter); - - // This is called at the end of the initial asset loading phase. - // Makes a guarantee that no new material nodes or parameter nodes will be added to the graph. - void finalize(); - - // This can be called after finalization to allow for dynamic addition of entities. - // It is slower than finalize() because it checks the readiness of existing materials. - void refinalize(); - - // These are called after textures have created and decoded. - void addEdge(filament::Texture* texture, Material* material, const char* parameter); - void markAsReady(filament::Texture* texture); - -private: - struct TextureNode { - filament::Texture* texture; - bool ready; - }; - - struct MaterialNode { - tsl::robin_map params; - }; - - struct EntityNode { - tsl::robin_set materials; - size_t numReadyMaterials = 0; - }; - - void checkReadiness(Material* material); - void markAsReady(Material* material); - TextureNode* getStatus(filament::Texture* texture); - - // The following maps contain the directed edges in the graph. - tsl::robin_map mEntityToMaterial; - tsl::robin_map> mMaterialToEntity; - tsl::robin_map mMaterialToTexture; - tsl::robin_map> mTextureToMaterial; - - // Each texture (and its readiness flag) can be referenced from multiple nodes, so we own - // a collection of wrapper objects in the following map. This uses std::unique_ptr to allow - // nodes to refer to a texture wrapper using a stable weak pointer. - tsl::robin_map> mTextureNodes; - - std::queue mReadyRenderables; - bool mFinalized = false; -}; - -} // namespace gltfio - -#endif // GLTFIO_DEPENDENCY_GRAPH_H diff --git a/ios/src/DracoCache.h b/ios/src/DracoCache.h deleted file mode 100644 index 88963da8..00000000 --- a/ios/src/DracoCache.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_DRACO_CACHE_H -#define GLTFIO_DRACO_CACHE_H - -#include - -#include - -#include - -#ifndef GLTFIO_DRACO_SUPPORTED -#define GLTFIO_DRACO_SUPPORTED 0 -#endif - -namespace gltfio { - -class DracoMesh; - -// Manages a set of Draco meshes that can be looked up using cgltf_buffer_view. -// -// The cache key is the buffer view that holds the compressed data. This allows the loader to -// avoid duplicated work when a single Draco mesh is referenced from multiple primitives. -class DracoCache { -public: - DracoMesh* findOrCreateMesh(const cgltf_buffer_view* key); -private: - tsl::robin_map> mCache; -}; - -// Decodes a Draco mesh upon construction and retains the results. -// -// The DracoMesh API leverages cgltf accessor structs in a way that bears explanation. These are -// read / write parameters that tell the decoder where to write the decoded data, and what format -// is desired. The buffer_view in the accessor should be null unless decompressed data is already -// loaded. This tells the decoder that it should create a buffer_view and a buffer. The buffer -// view, the buffer, and the buffer's data are all automatically freed when DracoMesh is destroyed. -// -// Note that in the gltfio architecture, the AssetLoader has the job of constructing VertexBuffer -// objects while the ResourceLoader has the job of populating them asychronously. This means that -// our Draco decoder relies on the accessor fields being 100% correct. If we had to be robust -// against faulty accessor information, we would need to replace the VertexBuffer object that was -// created in the AssetLoader, which would be a messy process. -class DracoMesh { -public: - static DracoMesh* decode(const uint8_t* compressedData, size_t compressedSize); - bool getFaceIndices(cgltf_accessor* destination) const; - bool getVertexAttributes(uint32_t attributeId, cgltf_accessor* destination) const; - ~DracoMesh(); -private: - DracoMesh(struct DracoMeshDetails* details); - std::unique_ptr mDetails; -}; - -} // namespace gltfio - -#endif // GLTFIO_DRACO_CACHE_H diff --git a/ios/src/FFilamentAsset.h b/ios/src/FFilamentAsset.h deleted file mode 100644 index a4061fe5..00000000 --- a/ios/src/FFilamentAsset.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_FFILAMENTASSET_H -#define GLTFIO_FFILAMENTASSET_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -#include "upcast.h" -#include "DependencyGraph.h" -#include "DracoCache.h" -#include "FFilamentInstance.h" - -#include -#include - -#include - -#ifdef NDEBUG -#define GLTFIO_VERBOSE 0 -#define GLTFIO_WARN(msg) -#else -#define GLTFIO_VERBOSE 1 -#define GLTFIO_WARN(msg) slog.w << msg << io::endl -#endif - -namespace utils { - class NameComponentManager; - class EntityManager; -} - -namespace gltfio { - -class Animator; -class Wireframe; -class MorphHelper; - -// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer(). -struct BufferSlot { - const cgltf_accessor* accessor; - cgltf_attribute_type attribute; - int bufferIndex; // for vertex buffers only - filament::VertexBuffer* vertexBuffer; - filament::IndexBuffer* indexBuffer; -}; - -// Encapsulates a connection between Texture and MaterialInstance. -struct TextureSlot { - const cgltf_texture* texture; - filament::MaterialInstance* materialInstance; - const char* materialParameter; - filament::TextureSampler sampler; - bool srgb; -}; - -// MeshCache -// --------- -// If a given glTF mesh is referenced by multiple glTF nodes, then it generates a separate Filament -// renderable for each of those nodes. All renderables generated by a given mesh share a common set -// of VertexBuffer and IndexBuffer objects. To achieve the sharing behavior, the loader maintains a -// small cache. The cache keys are glTF mesh definitions and the cache entries are lists of -// primitives, where a "primitive" is a reference to a Filament VertexBuffer and IndexBuffer. -struct Primitive { - filament::VertexBuffer* vertices = nullptr; - filament::IndexBuffer* indices = nullptr; - filament::Aabb aabb; // object-space bounding box - UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes) -}; -using MeshCache = tsl::robin_map>; - -// MatInstanceCache -// ---------------- -// Each glTF material definition corresponds to a single filament::MaterialInstance, which are -// temporarily cached during loading. The filament::Material objects that are used to create instances are -// cached in MaterialProvider. If a given glTF material is referenced by multiple glTF meshes, then -// their corresponding filament primitives will share the same Filament MaterialInstance and UvMap. -// The UvMap is a mapping from each texcoord slot in glTF to one of Filament's 2 texcoord sets. -struct MaterialEntry { - filament::MaterialInstance* instance; - UvMap uvmap; -}; -using MatInstanceCache = tsl::robin_map; - -struct FFilamentAsset : public FilamentAsset { - FFilamentAsset(filament::Engine* engine, utils::NameComponentManager* names, - utils::EntityManager* entityManager, const cgltf_data* srcAsset) : - mEngine(engine), mNameManager(names), mEntityManager(entityManager) { - mSourceAsset.reset(new SourceAsset {(cgltf_data*)srcAsset}); - } - - ~FFilamentAsset(); - - size_t getEntityCount() const noexcept { - return mEntities.size(); - } - - const utils::Entity* getEntities() const noexcept { - return mEntities.empty() ? nullptr : mEntities.data(); - } - - const utils::Entity* getLightEntities() const noexcept { - return mLightEntities.empty() ? nullptr : mLightEntities.data(); - } - - size_t getLightEntityCount() const noexcept { - return mLightEntities.size(); - } - - const utils::Entity* getCameraEntities() const noexcept { - return mCameraEntities.empty() ? nullptr : mCameraEntities.data(); - } - - size_t getCameraEntityCount() const noexcept { - return mCameraEntities.size(); - } - - utils::Entity getRoot() const noexcept { - return mRoot; - } - - size_t popRenderables(utils::Entity* entities, size_t count) noexcept { - return mDependencyGraph.popRenderables(entities, count); - } - - size_t getMaterialInstanceCount() const noexcept { - return mMaterialInstances.size(); - } - - const filament::MaterialInstance* const* getMaterialInstances() const noexcept { - return mMaterialInstances.data(); - } - - filament::MaterialInstance* const* getMaterialInstances() noexcept { - return mMaterialInstances.data(); - } - - size_t getResourceUriCount() const noexcept { - return mResourceUris.size(); - } - - const char* const* getResourceUris() const noexcept { - return mResourceUris.data(); - } - - filament::Aabb getBoundingBox() const noexcept { - return mBoundingBox; - } - - const char* getName(utils::Entity entity) const noexcept; - - const char* getExtras(utils::Entity entity) const noexcept; - - utils::Entity getFirstEntityByName(const char* name) noexcept; - - size_t getEntitiesByName(const char* name, utils::Entity* entities, - size_t maxCount) const noexcept; - - size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities, - size_t maxCount) const noexcept; - - Animator* getAnimator() noexcept; - - void setMorphWeights(utils::Entity entity , const float* weights, size_t count) noexcept; - - int getMorphTargetCount(utils::Entity entity) noexcept; - - utils::Entity getWireframe() noexcept; - - filament::Engine* getEngine() const noexcept { - return mEngine; - } - - void releaseSourceData() noexcept; - - const void* getSourceAsset() const noexcept { - return mSourceAsset.get() ? mSourceAsset->hierarchy : nullptr; - } - - FilamentInstance** getAssetInstances() noexcept { - return (FilamentInstance**) mInstances.data(); - } - - size_t getAssetInstanceCount() const noexcept { - return mInstances.size(); - } - - void takeOwnership(filament::Texture* texture) { - mTextures.push_back(texture); - } - - void bindTexture(const TextureSlot& tb, filament::Texture* texture) { - tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler); - mDependencyGraph.addEdge(texture, tb.materialInstance, tb.materialParameter); - } - - bool isInstanced() const { - return mInstances.size() > 0; - } - - filament::Engine* mEngine; - utils::NameComponentManager* mNameManager; - utils::EntityManager* mEntityManager; - std::vector mEntities; - std::vector mLightEntities; - std::vector mCameraEntities; - std::vector mMaterialInstances; - std::vector mVertexBuffers; - std::vector mBufferObjects; - std::vector mIndexBuffers; - std::vector mTextures; - filament::Aabb mBoundingBox; - utils::Entity mRoot; - std::vector mInstances; - SkinVector mSkins; // unused for instanced assets - Animator* mAnimator = nullptr; - MorphHelper* mMorpher = nullptr; - Wireframe* mWireframe = nullptr; - bool mResourcesLoaded = false; - DependencyGraph mDependencyGraph; - tsl::htrie_map> mNameToEntity; - tsl::robin_map mNodeExtras; - utils::CString mAssetExtras; - - // Sentinels for situations where ResourceLoader needs to generate data. - const cgltf_accessor mGenerateNormals = {}; - const cgltf_accessor mGenerateTangents = {}; - - // Encapsulates reference-counted source data, which includes the cgltf hierachy - // and potentially also includes buffer data that can be uploaded to the GPU. - struct SourceAsset { - ~SourceAsset() { cgltf_free(hierarchy); } - cgltf_data* hierarchy; - DracoCache dracoCache; - utils::FixedCapacityVector glbData; - }; - - // We used shared ownership for the raw cgltf data in order to permit ResourceLoader to - // complete various asynchronous work (e.g. uploading buffers to the GPU) even after the asset - // or ResourceLoader have been destroyed. - using SourceHandle = std::shared_ptr; - SourceHandle mSourceAsset; - - // Transient source data that can freed via releaseSourceData: - std::vector mBufferSlots; - std::vector mTextureSlots; - std::vector mResourceUris; - NodeMap mNodeMap; // unused for instanced assets - std::vector > mPrimitives; - MatInstanceCache mMatInstanceCache; - MeshCache mMeshCache; -}; - -FILAMENT_UPCAST(FilamentAsset) - -} // namespace gltfio - -#endif // GLTFIO_FFILAMENTASSET_H diff --git a/ios/src/FFilamentInstance.h b/ios/src/FFilamentInstance.h deleted file mode 100644 index 6487f6d1..00000000 --- a/ios/src/FFilamentInstance.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_FFILAMENTINSTANCE_H -#define GLTFIO_FFILAMENTINSTANCE_H - -#include - -#include - -#include - -#include - -#include -#include - -#include "upcast.h" - -struct cgltf_node; - -namespace gltfio { - -struct FFilamentAsset; -class Animator; - -struct Skin { - std::string name; - std::vector inverseBindMatrices; - std::vector joints; - std::vector targets; -}; - -using SkinVector = std::vector; -using NodeMap = tsl::robin_map; - -struct FFilamentInstance : public FilamentInstance { - std::vector entities; - utils::Entity root; - Animator* animator; - FFilamentAsset* owner; - SkinVector skins; - NodeMap nodeMap; - Animator* getAnimator() noexcept; -}; - -FILAMENT_UPCAST(FilamentInstance) - -} // namespace gltfio - -#endif // GLTFIO_FFILAMENTINSTANCE_H diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 59206927..f3636a34 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -41,13 +41,9 @@ #include #include -#include #include "math.h" -#include "FFilamentInstance.h" -#include "FFilamentAsset.h" - #include #include #include @@ -61,12 +57,6 @@ #include "Log.h" -#include -#include -#include -#include -#include - using namespace filament; using namespace filament::math; using namespace gltfio; @@ -84,18 +74,6 @@ namespace filament class LightManager; } -namespace gltfio -{ - MaterialProvider *createGPUMorphShaderLoader( - const void *opaqueData, - uint64_t opaqueDataSize, - const void *fadeData, - uint64_t fadeDataSize, - Engine *engine); - void decomposeMatrix(const filament::math::mat4f &mat, filament::math::float3 *translation, - filament::math::quatf *rotation, filament::math::float3 *scale); -} - namespace polyvox { @@ -136,14 +114,10 @@ namespace polyvox FilamentViewer::FilamentViewer( void *layer, - const char *opaqueShaderPath, - const char *fadeShaderPath, LoadResource loadResource, FreeResource freeResource) : _layer(layer), _loadResource(loadResource), _freeResource(freeResource), - opaqueShaderResources(nullptr, 0, 0), - fadeShaderResources(nullptr, 0, 0), _assetBuffer(nullptr, 0, 0) { _engine = Engine::create(Engine::Backend::OPENGL); @@ -205,7 +179,7 @@ namespace polyvox void FilamentViewer::createSwapChain(void *surface) { _swapChain = _engine->createSwapChain(surface); - // Log("swapchain created."); + Log("swapchain created."); } void FilamentViewer::destroySwapChain() @@ -214,8 +188,8 @@ namespace polyvox { _engine->destroy(_swapChain); _swapChain = nullptr; + Log("Swapchain destroyed."); } - // Log("swapchain destroyed."); } void FilamentViewer::applyWeights(float *weights, int count) @@ -223,8 +197,10 @@ namespace polyvox for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) { - _asset->setMorphWeights( - _asset->getEntities()[i], + RenderableManager &rm = _engine->getRenderableManager(); + auto inst = rm.getInstance(_asset->getEntities()[i]); + rm.setMorphWeights( + inst, weights, count); } @@ -240,6 +216,7 @@ namespace polyvox for (size_t i = 0; i < resourceUriCount; i++) { string uri = relativeResourcePath + string(resourceUris[i]); + Log("Creating resource buffer for resource at %s",uri.c_str()); ResourceBuffer buf = _loadResource(uri.c_str()); // using FunctionCallback = std::function; @@ -273,8 +250,6 @@ namespace polyvox { Log("Releasing source data"); _asset->releaseSourceData(); - // _freeResource(opaqueShaderResources); - // _freeResource(fadeShaderResources); } void FilamentViewer::loadGlb(const char *const uri) @@ -376,100 +351,99 @@ namespace polyvox /// bool FilamentViewer::setCamera(const char *cameraName) { - FFilamentAsset *asset = (FFilamentAsset *)_asset; - gltfio::NodeMap &sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap - : asset->mNodeMap; - Log("Setting camera to node %s", cameraName); - for (auto pair : sourceNodes) - { - cgltf_node const *node = pair.first; + // gltfio::NodeMap &sourceNodes = _asset->isInstanced() ? asset->mInstances[0]->nodeMap + // : asset->mNodeMap; + // Log("Setting camera to node %s", cameraName); + // for (auto pair : sourceNodes) + // { + // cgltf_node const *node = pair.first; - if (strcmp(cameraName, node->name) != 0) - { - continue; - } + // if (strcmp(cameraName, node->name) != 0) + // { + // continue; + // } - Log("Node %s : Matrix : %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f Translation : %03f %03f %03f Rotation %03f %03f %03f %03f Scale %03f %03f %03f", - node->name, - node->matrix[0], - node->matrix[1], - node->matrix[2], - node->matrix[3], - node->matrix[4], - node->matrix[5], - node->matrix[6], - node->matrix[7], - node->matrix[8], - node->matrix[9], - node->matrix[10], - node->matrix[11], - node->matrix[12], - node->matrix[13], - node->matrix[14], - node->matrix[15], - node->translation[0], - node->translation[1], - node->translation[2], - node->rotation[0], - node->rotation[1], - node->rotation[2], - node->rotation[3], - node->scale[0], - node->scale[1], - node->scale[2] - ); - mat4f t = mat4f::translation(float3 { node->translation[0],node->translation[1],node->translation[2] }); - mat4f r { quatf { node->rotation[3], node->rotation[0], node->rotation[1], node->rotation[2] } }; - mat4f transform = t * r; + // Log("Node %s : Matrix : %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f %03f Translation : %03f %03f %03f Rotation %03f %03f %03f %03f Scale %03f %03f %03f", + // node->name, + // node->matrix[0], + // node->matrix[1], + // node->matrix[2], + // node->matrix[3], + // node->matrix[4], + // node->matrix[5], + // node->matrix[6], + // node->matrix[7], + // node->matrix[8], + // node->matrix[9], + // node->matrix[10], + // node->matrix[11], + // node->matrix[12], + // node->matrix[13], + // node->matrix[14], + // node->matrix[15], + // node->translation[0], + // node->translation[1], + // node->translation[2], + // node->rotation[0], + // node->rotation[1], + // node->rotation[2], + // node->rotation[3], + // node->scale[0], + // node->scale[1], + // node->scale[2] + // ); + // mat4f t = mat4f::translation(float3 { node->translation[0],node->translation[1],node->translation[2] }); + // mat4f r { quatf { node->rotation[3], node->rotation[0], node->rotation[1], node->rotation[2] } }; + // mat4f transform = t * r; - if (!node->camera) - { - cgltf_node* leaf = node->children[0]; + // if (!node->camera) + // { + // cgltf_node* leaf = node->children[0]; - Log("Child 1 trans : %03f %03f %03f rot : %03f %03f %03f %03f ", leaf->translation[0], leaf->translation[1],leaf->translation[2], leaf->rotation[0],leaf->rotation[1],leaf->rotation[2],leaf->rotation[3]); + // Log("Child 1 trans : %03f %03f %03f rot : %03f %03f %03f %03f ", leaf->translation[0], leaf->translation[1],leaf->translation[2], leaf->rotation[0],leaf->rotation[1],leaf->rotation[2],leaf->rotation[3]); - if (!leaf->camera) { - leaf = leaf->children[0]; - Log("Child 2 %03f %03f %03f %03f %03f %03f %03f ", leaf->translation[0], leaf->translation[1],leaf->translation[2], leaf->rotation[0],leaf->rotation[1],leaf->rotation[2],leaf->rotation[3]); - if (!leaf->camera) { - Log("Could not find GLTF camera under node or its ssecond or third child nodes."); - exit(-1); - } - } + // if (!leaf->camera) { + // leaf = leaf->children[0]; + // Log("Child 2 %03f %03f %03f %03f %03f %03f %03f ", leaf->translation[0], leaf->translation[1],leaf->translation[2], leaf->rotation[0],leaf->rotation[1],leaf->rotation[2],leaf->rotation[3]); + // if (!leaf->camera) { + // Log("Could not find GLTF camera under node or its ssecond or third child nodes."); + // exit(-1); + // } + // } - Log("Using rotation from leaf node."); + // Log("Using rotation from leaf node."); - mat4f child_rot { quatf { leaf->rotation[3], leaf->rotation[0], leaf->rotation[1], leaf->rotation[2] } }; + // mat4f child_rot { quatf { leaf->rotation[3], leaf->rotation[0], leaf->rotation[1], leaf->rotation[2] } }; - transform *= child_rot; - } + // transform *= child_rot; + // } - Entity cameraEntity = EntityManager::get().create(); - Camera *cam = _engine->createCamera(cameraEntity); + // Entity cameraEntity = EntityManager::get().create(); + // Camera *cam = _engine->createCamera(cameraEntity); - const Viewport &vp = _view->getViewport(); + // const Viewport &vp = _view->getViewport(); - const double aspect = (double)vp.width / vp.height; + // const double aspect = (double)vp.width / vp.height; - // todo - pull focal length from gltf node + // // todo - pull focal length from gltf node - cam->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane); + // cam->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane); - if (!cam) - { - Log("Couldn't create camera"); - } - else - { - _engine->getTransformManager().setTransform( - _engine->getTransformManager().getInstance(cameraEntity), transform - ); + // if (!cam) + // { + // Log("Couldn't create camera"); + // } + // else + // { + // _engine->getTransformManager().setTransform( + // _engine->getTransformManager().getInstance(cameraEntity), transform + // ); - _view->setCamera(cam); - return true; - } - } + // _view->setCamera(cam); + // return true; + // } + // } return false; } @@ -492,34 +466,12 @@ namespace polyvox StringList FilamentViewer::getTargetNames(const char *meshName) { - FFilamentAsset *asset = (FFilamentAsset *)_asset; - NodeMap &sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap; - - if (sourceNodes.empty()) - { - Log("Asset source nodes empty?"); - return StringList(nullptr, 0); - } - Log("Fetching morph target names for mesh %s", meshName); - - for (auto pair : sourceNodes) - { - cgltf_node const *node = pair.first; - cgltf_mesh const *mesh = node->mesh; - - if (mesh) - { - Log("Mesh : %s ", mesh->name); - if (strcmp(meshName, mesh->name) == 0) - { - return StringList((const char **)mesh->target_names, (int)mesh->target_names_count); - } - } - } + // int count = asset->getMorphTargetCountAt() + // asset->getMorphTargetNameAt() return StringList(nullptr, 0); } - void FilamentViewer::loadSkybox(const char *const skyboxPath, const char *const iblPath, AAssetManager *am) + void FilamentViewer::loadSkybox(const char *const skyboxPath, const char *const iblPath) { ResourceBuffer skyboxBuffer = _loadResource(skyboxPath); @@ -633,7 +585,8 @@ namespace polyvox const double aspect = (double)width / height; _mainCamera->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane); - Log("Set viewport to %d %d", _width, _height); + + Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, contentScaleFactor); } void FilamentViewer::animateWeights(float *data, int numWeights, int numFrames, float frameRate) @@ -700,74 +653,3 @@ namespace polyvox } - -// // -// //if(morphAnimationBuffer.frameIndex >= morphAnimationBuffer.numFrames) { -// // this.morphAnimationBuffer = null; -// // return; -// //} -// // -// //if(morphAnimationBuffer.frameIndex == -1) { -// // applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); -// // morphAnimationBuffer->frameIndex++; -// // morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now(); -// //} else { -// // duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime; -// // float msElapsed = dur.count(); -// // if(msElapsed > morphAnimationBuffer->frameLength) { -// // frameIndex++; -// // applyWeights(frameData + (frameIndex * numWeights), numWeights); -// // morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now(); -// // } -// //} - - -// void FilamentViewer::createMorpher(const char* meshName, int* primitives, int numPrimitives) { -// // morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, primitives, numPrimitives); -// // morphHelper = new gltfio::CPUMorpher(((FFilamentAsset)*_asset, (FFilamentInstance*)_asset)); -// } - -// void FilamentViewer::animateBones() { -// } -// Entity entity = _asset->getFirstEntityByName("CC_Base_JawRoot"); -// if(!entity) { -// return; -// } - -// TransformManager& transformManager = _engine->getTransformManager(); - -// TransformManager::Instance node = transformManager.getInstance( entity); - -// mat4f xform = transformManager.getTransform(node); -// float3 scale; -// quatf rotation; -// float3 translation; -// decomposeMatrix(xform, &translation, &rotation, &scale); - -// // const quatf srcQuat { weights[0] * 0.9238,0,weights[0] * 0.3826, 0 }; -// // float3 { scale[0] * (1.0f - weights[0]), scale[1] * (1.0f - weights[1]), scale[2] * (1.0f - weights[2]) } -// // xform = composeMatrix(translation + float3 { weights[0], weights[1], weights[2] }, rotation, scale ); -// transformManager.setTransform(node, xform); - -// } - -// void FilamentViewer::updateAnimation(AnimationBuffer animation, std::function callback) { -// if(morphAnimationBuffer.frameIndex >= animation.numFrames) { -// this.animation = null; -// return; -// } - -// if(animation.frameIndex == -1) { -// animation->frameIndex++; -// animation->lastTime = std::chrono::high_resolution_clock::now(); -// callback(); // applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights); -// } else { -// duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime; -// float msElapsed = dur.count(); -// if(msElapsed > animation->frameLength) { -// animation->frameIndex++; -// animation->lastTime = std::chrono::high_resolution_clock::now(); -// callback(); // applyWeights(frameData + (frameIndex * numWeights), numWeights); -// } -// } -// } diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp index e338c3c2..34b3b7dc 100644 --- a/ios/src/FilamentViewer.hpp +++ b/ios/src/FilamentViewer.hpp @@ -30,12 +30,6 @@ #include #include -#include -#include -#include -#include -#include - using namespace std; using namespace filament; using namespace filament::math; @@ -102,22 +96,21 @@ namespace polyvox { class FilamentViewer { public: - FilamentViewer(void* layer, const char* opaqueShaderPath, const char* fadeShaderPath, LoadResource loadResource, FreeResource freeResource); + FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource); ~FilamentViewer(); void loadGlb(const char* const uri); void loadGltf(const char* const uri, const char* relativeResourcePath); - void loadSkybox(const char* const skyboxUri, const char* const iblUri, AAssetManager* am); + void loadSkybox(const char* const skyboxUri, const char* const iblUri); void updateViewportAndCameraProjection(int height, int width, float scaleFactor); void render(); - // void createMorpher(const char* meshName, int* primitives, int numPrimitives); void releaseSourceAssets(); StringList getTargetNames(const char* meshName); unique_ptr> getAnimationNames(); Manipulator* manipulator; void applyWeights(float* weights, int count); void animateWeights(float* data, int numWeights, int length, float frameRate); - // void animateBones(); + void playAnimation(int index, bool loop); bool setCamera(const char* nodeName); void destroySwapChain(); @@ -136,9 +129,6 @@ namespace polyvox { LoadResource _loadResource; FreeResource _freeResource; - ResourceBuffer opaqueShaderResources; - ResourceBuffer fadeShaderResources; - Scene* _scene; View* _view; Engine* _engine; diff --git a/ios/src/GltfEnums.h b/ios/src/GltfEnums.h deleted file mode 100644 index 39e71b7a..00000000 --- a/ios/src/GltfEnums.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_GLTFENUMS_H -#define GLTFIO_GLTFENUMS_H - -#include -#include -#include -#include - -#include - -#define GL_NEAREST 0x2600 -#define GL_LINEAR 0x2601 -#define GL_NEAREST_MIPMAP_NEAREST 0x2700 -#define GL_LINEAR_MIPMAP_NEAREST 0x2701 -#define GL_NEAREST_MIPMAP_LINEAR 0x2702 -#define GL_LINEAR_MIPMAP_LINEAR 0x2703 -#define GL_REPEAT 0x2901 -#define GL_MIRRORED_REPEAT 0x8370 -#define GL_CLAMP_TO_EDGE 0x812F - -inline filament::TextureSampler::WrapMode getWrapMode(cgltf_int wrap) { - switch (wrap) { - case GL_REPEAT: - return filament::TextureSampler::WrapMode::REPEAT; - case GL_MIRRORED_REPEAT: - return filament::TextureSampler::WrapMode::MIRRORED_REPEAT; - case GL_CLAMP_TO_EDGE: - return filament::TextureSampler::WrapMode::CLAMP_TO_EDGE; - } - return filament::TextureSampler::WrapMode::REPEAT; -} - -inline filament::TextureSampler::MinFilter getMinFilter(cgltf_int minFilter) { - switch (minFilter) { - case GL_NEAREST: - return filament::TextureSampler::MinFilter::NEAREST; - case GL_LINEAR: - return filament::TextureSampler::MinFilter::LINEAR; - case GL_NEAREST_MIPMAP_NEAREST: - return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_NEAREST; - case GL_LINEAR_MIPMAP_NEAREST: - return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_NEAREST; - case GL_NEAREST_MIPMAP_LINEAR: - return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_LINEAR; - case GL_LINEAR_MIPMAP_LINEAR: - return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR; - } - return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR; -} - -inline filament::TextureSampler::MagFilter getMagFilter(cgltf_int magFilter) { - switch (magFilter) { - case GL_NEAREST: - return filament::TextureSampler::MagFilter::NEAREST; - case GL_LINEAR: - return filament::TextureSampler::MagFilter::LINEAR; - } - return filament::TextureSampler::MagFilter::LINEAR; -} - -inline bool getVertexAttrType(cgltf_attribute_type atype, filament::VertexAttribute* attrType) { - switch (atype) { - case cgltf_attribute_type_position: - *attrType = filament::VertexAttribute::POSITION; - return true; - case cgltf_attribute_type_texcoord: - *attrType = filament::VertexAttribute::UV0; - return true; - case cgltf_attribute_type_color: - *attrType = filament::VertexAttribute::COLOR; - return true; - case cgltf_attribute_type_joints: - *attrType = filament::VertexAttribute::BONE_INDICES; - return true; - case cgltf_attribute_type_weights: - *attrType = filament::VertexAttribute::BONE_WEIGHTS; - return true; - case cgltf_attribute_type_normal: - case cgltf_attribute_type_tangent: - default: - return false; - } -} - -inline bool getIndexType(cgltf_component_type ctype, filament::IndexBuffer::IndexType* itype) { - switch (ctype) { - case cgltf_component_type_r_8u: - case cgltf_component_type_r_16u: - *itype = filament::IndexBuffer::IndexType::USHORT; - return true; - case cgltf_component_type_r_32u: - *itype = filament::IndexBuffer::IndexType::UINT; - return true; - default: - break; - } - return false; -} - -inline bool getPrimitiveType(cgltf_primitive_type in, - filament::RenderableManager::PrimitiveType* out) { - switch (in) { - case cgltf_primitive_type_points: - *out = filament::RenderableManager::PrimitiveType::POINTS; - return true; - case cgltf_primitive_type_lines: - *out = filament::RenderableManager::PrimitiveType::LINES; - return true; - case cgltf_primitive_type_line_strip: - *out = filament::RenderableManager::PrimitiveType::LINE_STRIP; - return true; - case cgltf_primitive_type_triangles: - *out = filament::RenderableManager::PrimitiveType::TRIANGLES; - return true; - case cgltf_primitive_type_triangle_strip: - *out = filament::RenderableManager::PrimitiveType::TRIANGLE_STRIP; - return true; - case cgltf_primitive_type_line_loop: - case cgltf_primitive_type_triangle_fan: - return false; - } - return false; -} - -// This converts a cgltf component type into a Filament Attribute type. -// -// This function has two out parameters. One result is a safe "permitted type" which we know is -// universally accepted across GPU's and backends, but may require conversion (see Transcoder). The -// other result is the "actual type" which requires no conversion. -// -// Returns false if the given component type is invalid. -inline bool getElementType(cgltf_type type, cgltf_component_type ctype, - filament::VertexBuffer::AttributeType* permitType, - filament::VertexBuffer::AttributeType* actualType) { - switch (type) { - case cgltf_type_scalar: - switch (ctype) { - case cgltf_component_type_r_8: - *permitType = filament::VertexBuffer::AttributeType::BYTE; - *actualType = filament::VertexBuffer::AttributeType::BYTE; - return true; - case cgltf_component_type_r_8u: - *permitType = filament::VertexBuffer::AttributeType::UBYTE; - *actualType = filament::VertexBuffer::AttributeType::UBYTE; - return true; - case cgltf_component_type_r_16: - *permitType = filament::VertexBuffer::AttributeType::SHORT; - *actualType = filament::VertexBuffer::AttributeType::SHORT; - return true; - case cgltf_component_type_r_16u: - *permitType = filament::VertexBuffer::AttributeType::USHORT; - *actualType = filament::VertexBuffer::AttributeType::USHORT; - return true; - case cgltf_component_type_r_32u: - *permitType = filament::VertexBuffer::AttributeType::UINT; - *actualType = filament::VertexBuffer::AttributeType::UINT; - return true; - case cgltf_component_type_r_32f: - *permitType = filament::VertexBuffer::AttributeType::FLOAT; - *actualType = filament::VertexBuffer::AttributeType::FLOAT; - return true; - default: - return false; - } - break; - case cgltf_type_vec2: - switch (ctype) { - case cgltf_component_type_r_8: - *permitType = filament::VertexBuffer::AttributeType::BYTE2; - *actualType = filament::VertexBuffer::AttributeType::BYTE2; - return true; - case cgltf_component_type_r_8u: - *permitType = filament::VertexBuffer::AttributeType::UBYTE2; - *actualType = filament::VertexBuffer::AttributeType::UBYTE2; - return true; - case cgltf_component_type_r_16: - *permitType = filament::VertexBuffer::AttributeType::SHORT2; - *actualType = filament::VertexBuffer::AttributeType::SHORT2; - return true; - case cgltf_component_type_r_16u: - *permitType = filament::VertexBuffer::AttributeType::USHORT2; - *actualType = filament::VertexBuffer::AttributeType::USHORT2; - return true; - case cgltf_component_type_r_32f: - *permitType = filament::VertexBuffer::AttributeType::FLOAT2; - *actualType = filament::VertexBuffer::AttributeType::FLOAT2; - return true; - default: - return false; - } - break; - case cgltf_type_vec3: - switch (ctype) { - case cgltf_component_type_r_8: - *permitType = filament::VertexBuffer::AttributeType::FLOAT3; - *actualType = filament::VertexBuffer::AttributeType::BYTE3; - return true; - case cgltf_component_type_r_8u: - *permitType = filament::VertexBuffer::AttributeType::FLOAT3; - *actualType = filament::VertexBuffer::AttributeType::UBYTE3; - return true; - case cgltf_component_type_r_16: - *permitType = filament::VertexBuffer::AttributeType::FLOAT3; - *actualType = filament::VertexBuffer::AttributeType::SHORT3; - return true; - case cgltf_component_type_r_16u: - *permitType = filament::VertexBuffer::AttributeType::FLOAT3; - *actualType = filament::VertexBuffer::AttributeType::USHORT3; - return true; - case cgltf_component_type_r_32f: - *permitType = filament::VertexBuffer::AttributeType::FLOAT3; - *actualType = filament::VertexBuffer::AttributeType::FLOAT3; - return true; - default: - return false; - } - break; - case cgltf_type_vec4: - switch (ctype) { - case cgltf_component_type_r_8: - *permitType = filament::VertexBuffer::AttributeType::BYTE4; - *actualType = filament::VertexBuffer::AttributeType::BYTE4; - return true; - case cgltf_component_type_r_8u: - *permitType = filament::VertexBuffer::AttributeType::UBYTE4; - *actualType = filament::VertexBuffer::AttributeType::UBYTE4; - return true; - case cgltf_component_type_r_16: - *permitType = filament::VertexBuffer::AttributeType::SHORT4; - *actualType = filament::VertexBuffer::AttributeType::SHORT4; - return true; - case cgltf_component_type_r_16u: - *permitType = filament::VertexBuffer::AttributeType::USHORT4; - *actualType = filament::VertexBuffer::AttributeType::USHORT4; - return true; - case cgltf_component_type_r_32f: - *permitType = filament::VertexBuffer::AttributeType::FLOAT4; - *actualType = filament::VertexBuffer::AttributeType::FLOAT4; - return true; - default: - return false; - } - break; - default: - return false; - } - return false; -} - -inline bool requiresConversion(cgltf_type type, cgltf_component_type ctype) { - filament::VertexBuffer::AttributeType permitted; - filament::VertexBuffer::AttributeType actual; - bool supported = getElementType(type, ctype, &permitted, &actual); - return supported && permitted != actual; -} - -#endif // GLTFIO_GLTFENUMS_H diff --git a/ios/src/Log.h b/ios/src/Log.h index 8acfc0e9..b487fa19 100644 --- a/ios/src/Log.h +++ b/ios/src/Log.h @@ -16,10 +16,10 @@ void Log(const char *fmt, ...) { #elif defined __OBJC__ NSString *format = [[NSString alloc] initWithUTF8String:fmt]; NSLogv(format, args); - [format release]; #else - printf(fmt, args); + vprintf(fmt, args); + printf("\n"); #endif va_end(args); -} \ No newline at end of file +} diff --git a/ios/src/MorphHelper.h b/ios/src/MorphHelper.h deleted file mode 100644 index 45c51ba0..00000000 --- a/ios/src/MorphHelper.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_MORPHHELPER_H -#define GLTFIO_MORPHHELPER_H - -#include "FFilamentAsset.h" -#include "FFilamentInstance.h" - -#include - -#include - -#include - -#include - -struct cgltf_node; -struct cgltf_mesh; -struct cgltf_primitive; - -namespace gltfio { - -/** - * Internal class that partitions lists of morph weights and maintains a cache of BufferObject - * instances. This allows gltfio to support up to 255 morph targets. - * - * Each partition is associated with an unordered set of 4 (or fewer) morph target indices, which - * we call the "primary indices" for that time slice. - * - * Animator has ownership over a single instance of MorphHelper, thus it is 1:1 with FilamentAsset. - */ -class MorphHelper { -public: - using Entity = utils::Entity; - MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst); - ~MorphHelper(); - - void setWeights(Entity entity, float const* weights, int count) noexcept; - int getTargetCount(Entity entity) const noexcept; - -private: - struct GltfPrimitive { - filament::MorphTargetBuffer* targets; - }; - - struct TableEntry { - std::vector primitives; // TODO: flatten this? - }; - - void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Entity entity); - - tsl::robin_map mMorphTable; - const FFilamentAsset* mAsset; - const FFilamentInstance* mInstance; -}; - -} // namespace gltfio - -#endif // GLTFIO_MORPHHELPER_H diff --git a/ios/src/TangentsJob.h b/ios/src/TangentsJob.h deleted file mode 100644 index 06e8f67f..00000000 --- a/ios/src/TangentsJob.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -namespace filament { class VertexBuffer; } - -namespace gltfio { - -/** - * Internal helper that examines a cgltf primitive and generates data suitable for Filament's - * TANGENTS attribute. This has been designed to be run as a JobSystem job, but clients are not - * required to do so. - */ -struct TangentsJob { - static constexpr int kMorphTargetUnused = -1; - - // The inputs to the procedure. The prim is owned by the client, which should ensure that it - // stays alive for the duration of the procedure. - struct InputParams { - const cgltf_primitive* prim; - const int morphTargetIndex = kMorphTargetUnused; - }; - - // The context of the procedure. These fields are not used by the procedure but are provided as - // a convenience to clients. You can think of this as a scratch space for clients. - struct Context { - filament::VertexBuffer* const vb; - const uint8_t slot; - }; - - // The outputs of the procedure. The results array gets malloc'd by the procedure, so clients - // should remember to free it. - struct OutputParams { - cgltf_size vertexCount; - filament::math::short4* results; - }; - - // Clients might want to track the jobs in an array, so the arguments are bundled into a struct. - struct Params { - InputParams in; - Context context; - OutputParams out; - }; - - // Performs tangents generation synchronously. This can be invoked from inside a job if desired. - // The parameters structure is owned by the client. - static void run(Params* params); -}; - -} // namespace gltfio diff --git a/ios/src/Wireframe.h b/ios/src/Wireframe.h deleted file mode 100644 index dc083561..00000000 --- a/ios/src/Wireframe.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef GLTFIO_WIREFRAME_H -#define GLTFIO_WIREFRAME_H - -#include -#include - -#include - -namespace gltfio { - -struct FFilamentAsset; - -struct Wireframe { - Wireframe(FFilamentAsset* asset); - ~Wireframe(); - const FFilamentAsset* mAsset; - utils::Entity mEntity; - filament::VertexBuffer* mVertexBuffer; - filament::IndexBuffer* mIndexBuffer; -}; - -} // namsepace gltfio - -#endif // GLTFIO_WIREFRAME_H diff --git a/ios/src/upcast.h b/ios/src/upcast.h deleted file mode 100644 index dd454086..00000000 --- a/ios/src/upcast.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_FILAMENT_UPCAST_H -#define TNT_FILAMENT_UPCAST_H - -/* - * Generates functions to safely upcast a pointer Bar* to FBar* - * FILAMENT_UPCAST() should be included in the header file - * declaring FBar, e.g.: - * - * #include - * - * class FBar : public Bar { - * }; - * - * FILAMENT_UPCAST(Bar) - * - */ - -#define FILAMENT_UPCAST(CLASS) \ - inline F##CLASS& upcast(CLASS& that) noexcept { \ - return static_cast(that); \ - } \ - inline const F##CLASS& upcast(const CLASS& that) noexcept { \ - return static_cast(that); \ - } \ - inline F##CLASS* upcast(CLASS* that) noexcept { \ - return static_cast(that); \ - } \ - inline F##CLASS const* upcast(CLASS const* that) noexcept { \ - return static_cast(that); \ - } - -#endif // TNT_FILAMENT_UPCAST_H diff --git a/pubspec.yaml b/pubspec.yaml index 440b955d..141bacb6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: polyvox_filament -description: The 3D rendering layer for the Polyvox app. +description: A Flutter plugin to wrap the Filament rendering engine. version: 0.0.1 homepage: @@ -10,7 +10,6 @@ environment: dependencies: flutter: sdk: flutter - ffi: plugin_platform_interface: ^2.0.0 @@ -21,10 +20,6 @@ dev_dependencies: flutter: - #assets: - #- assets/compiled.mat - ##- assets/lit_opaque.filamat - #- assets/lit_fade.filamat plugin: platforms: android: