upgrade to Filament 1.21.0
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -20,8 +20,6 @@ interface FilamentInterop : Library {
|
||||
|
||||
fun filament_viewer_new(
|
||||
layer:Object,
|
||||
opaqueShaderPath:String,
|
||||
fadeShaderPath:String,
|
||||
env:JNIEnv,
|
||||
am:AssetManager
|
||||
) : Pointer;
|
||||
|
||||
@@ -3,21 +3,18 @@
|
||||
#endif /* FilamentNativeViewFactory_h */
|
||||
|
||||
#import <Flutter/Flutter.h>
|
||||
#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<FlutterPluginRegistrar>*)registrar
|
||||
viewId:(int64_t)viewId
|
||||
layer:(void*)layer;
|
||||
|
||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar viewId:(int64_t)viewId viewer:(FilamentViewer*)viewer;
|
||||
@end
|
||||
|
||||
@@ -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<FlutterPluginRegistrar>* _registrar;
|
||||
}
|
||||
- (instancetype)initWithController:(FilamentViewController*)controller
|
||||
registrar:(NSObject<FlutterPluginRegistrar>*)registrar
|
||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)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 {
|
||||
|
||||
@@ -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<FlutterPluginRegistrar>* _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<FlutterPluginRegistrar>*)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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,22 +20,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <UIKit/UIKit.h>
|
||||
|
||||
#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<FlutterPluginRegistrar>*)registrar view:(FilamentView*)view;
|
||||
|
||||
@end
|
||||
@@ -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 <Flutter/Flutter.h>
|
||||
|
||||
@implementation FilamentViewController {
|
||||
CADisplayLink* _displayLink;
|
||||
NSObject<FlutterPluginRegistrar>* _registrar;
|
||||
polyvox::FilamentViewer* _viewer;
|
||||
FilamentView* _view;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)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
|
||||
@@ -1,15 +0,0 @@
|
||||
#import "PolyvoxFilamentPlugin.h"
|
||||
#if __has_include(<polyvox_filament/polyvox_filament-Swift.h>)
|
||||
#import <polyvox_filament/polyvox_filament-Swift.h>
|
||||
#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<FlutterPluginRegistrar>*)registrar {
|
||||
[SwiftPolyvoxFilamentPlugin registerWithRegistrar:registrar];
|
||||
}
|
||||
@end
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 $<$<CONFIG:Release>:/fp:fast>)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:-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()
|
||||
@@ -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
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
#define TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#if !defined(NDEBUG)
|
||||
#include <utils/Log.h>
|
||||
#endif
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
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 <typename T>
|
||||
/**
|
||||
* Type-safe handle to backend resources
|
||||
* @tparam T Type of the resource
|
||||
*/
|
||||
template<typename T>
|
||||
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<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
|
||||
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions)
|
||||
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
|
||||
|
||||
private:
|
||||
#if !defined(NDEBUG)
|
||||
@@ -112,7 +130,6 @@ using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
|
||||
@@ -22,38 +22,19 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
//! \privatesection
|
||||
|
||||
class TargetBufferInfo {
|
||||
public:
|
||||
// ctor for 2D textures
|
||||
TargetBufferInfo(Handle<HwTexture> h, uint8_t level = 0) noexcept // NOLINT(google-explicit-constructor)
|
||||
: handle(h), level(level) { }
|
||||
// ctor for cubemaps
|
||||
TargetBufferInfo(Handle<HwTexture> h, uint8_t level, TextureCubemapFace face) noexcept
|
||||
: handle(h), level(level), face(face) { }
|
||||
// ctor for 3D textures
|
||||
TargetBufferInfo(Handle<HwTexture> 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<HwTexture> 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<HwTexture> h, uint8_t level, uint16_t layer) noexcept
|
||||
: mInfos{{ h, level, layer }} {
|
||||
MRT(Handle<HwTexture> 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 <jni.h>
|
||||
|
||||
#include "common/NioUtils.h"
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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 <algorithm>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
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<void *>(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);
|
||||
}
|
||||
@@ -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 <cstdint>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
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{};
|
||||
};
|
||||
96
ios/include/filamat/Enums.h
Normal file
96
ios/include/filamat/Enums.h
Normal file
@@ -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 <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
|
||||
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<typename T>
|
||||
static bool isValid(const std::string& s) noexcept {
|
||||
std::unordered_map<std::string, T>& map = getMap<T>();
|
||||
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<typename T>
|
||||
static T toEnum(const std::string& s) noexcept {
|
||||
std::unordered_map<std::string, T>& map = getMap<T>();
|
||||
return map.at(s);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static std::string toString(T t) noexcept;
|
||||
|
||||
// Return a map of all values in an enum with their string representation.
|
||||
template<typename T>
|
||||
static std::unordered_map<std::string, T>& map() noexcept {
|
||||
std::unordered_map<std::string, T>& map = getMap<T>();
|
||||
return map;
|
||||
};
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static std::unordered_map<std::string, T>& getMap() noexcept;
|
||||
|
||||
static std::unordered_map<std::string, Property> mStringToProperty;
|
||||
static std::unordered_map<std::string, UniformType> mStringToUniformType;
|
||||
static std::unordered_map<std::string, SamplerType> mStringToSamplerType;
|
||||
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
|
||||
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
|
||||
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
|
||||
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
|
||||
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
|
||||
static std::unordered_map<std::string, OutputType> mStringToOutputType;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::string Enums::toString(T t) noexcept {
|
||||
std::unordered_map<std::string, T>& map = getMap<T>();
|
||||
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
|
||||
71
ios/include/filamat/IncludeCallback.h
Normal file
71
ios/include/filamat/IncludeCallback.h
Normal file
@@ -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 <utils/CString.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
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<bool(
|
||||
const utils::CString& includedBy,
|
||||
IncludeResult& result)>;
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
#endif
|
||||
764
ios/include/filamat/MaterialBuilder.h
Normal file
764
ios/include/filamat/MaterialBuilder.h
Normal file
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <filamat/IncludeCallback.h>
|
||||
#include <filamat/Package.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
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<CodeGenParams> 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<int> 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 <filamat/MaterialBuilder.h>
|
||||
* 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<PreprocessorDefine>;
|
||||
|
||||
//! 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<Output>;
|
||||
|
||||
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<filamat::Variant>& 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<filamat::MaterialBuilder::TargetApi>
|
||||
: public std::true_type {};
|
||||
|
||||
#endif
|
||||
103
ios/include/filamat/Package.h
Normal file
103
ios/include/filamat/Package.h
Normal file
@@ -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 <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
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
|
||||
242
ios/include/filament-iblprefilter/IBLPrefilterContext.h
Normal file
242
ios/include/filament-iblprefilter/IBLPrefilterContext.h
Normal file
@@ -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 <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <filament/Texture.h>
|
||||
|
||||
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 <filament/Engine.h>
|
||||
* 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
|
||||
@@ -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.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_FILAMENTAPI_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -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 <typename T>
|
||||
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<typename ... ARGS>
|
||||
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<typename T>
|
||||
using BuilderBase = utils::PrivateImplementation<T>;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <filament/Box.h>
|
||||
#include <filament/FilamentAPI.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -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<typename VECTOR, typename INDEX, typename, typename>
|
||||
Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
|
||||
size_t stride) noexcept {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
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<void(utils::Entity entity)>&& functor) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -70,13 +70,19 @@ class UTILS_PUBLIC TransformManager : public FilamentAPI {
|
||||
public:
|
||||
using Instance = utils::EntityInstance<TransformManager>;
|
||||
|
||||
class children_iterator : std::iterator<std::forward_iterator_tag, Instance> {
|
||||
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
|
||||
|
||||
@@ -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<typename T, void(T::*method)(PickingQueryResult const&)>
|
||||
@@ -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<typename T, void(T::*method)(PickingQueryResult const&)>
|
||||
|
||||
120
ios/include/filameshio/MeshReader.h
Normal file
120
ios/include/filameshio/MeshReader.h
Normal file
@@ -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 <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
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
|
||||
131
ios/include/geometry/SurfaceOrientation.h
Normal file
131
ios/include/geometry/SurfaceOrientation.h
Normal file
@@ -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 <math/quat.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
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
|
||||
104
ios/include/geometry/Transcoder.h
Normal file
104
ios/include/geometry/Transcoder.h
Normal file
@@ -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 <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 <stb_image.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.
|
||||
|
||||
126
ios/include/gltfio/math.h
Normal file
126
ios/include/gltfio/math.h
Normal file
@@ -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 <math/quat.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/mat3.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/TVecHelpers.h>
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
template <typename T>
|
||||
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<float>::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
|
||||
46
ios/include/gltfio/resources/gltfresources.h
Normal file
46
ios/include/gltfio/resources/gltfresources.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef GLTFRESOURCES_H_
|
||||
#define GLTFRESOURCES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
16
ios/include/gltfio/resources/gltfresources_lite.h
Normal file
16
ios/include/gltfio/resources/gltfresources_lite.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef GLTFRESOURCES_LITE_H_
|
||||
#define GLTFRESOURCES_LITE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
199
ios/include/ibl/Cubemap.h
Normal file
199
ios/include/ibl/Cubemap.h
Normal file
@@ -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 <ibl/Image.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec2.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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<Texel const*>(data);
|
||||
}
|
||||
|
||||
//! writes a texel at a given address
|
||||
inline static void writeAt(void* data, const Texel& texel) {
|
||||
*static_cast<Texel*>(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 */
|
||||
91
ios/include/ibl/CubemapIBL.h
Normal file
91
ios/include/ibl/CubemapIBL.h
Normal file
@@ -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 <math/vec3.h>
|
||||
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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<Cubemap>& 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<Cubemap>& 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<Cubemap>& 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 */
|
||||
125
ios/include/ibl/CubemapSH.h
Normal file
125
ios/include/ibl/CubemapSH.h
Normal file
@@ -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 <utils/compiler.h>
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
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<math::float3[]> 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<math::float3[]>& sh, size_t numBands);
|
||||
|
||||
static void windowSH(std::unique_ptr<math::float3[]>& 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<math::float3[]>& sh);
|
||||
|
||||
/**
|
||||
* Render pre-scaled irrandiance SH
|
||||
*/
|
||||
static void renderPreScaledSH3Bands(utils::JobSystem& js, Cubemap& cm,
|
||||
const std::unique_ptr<math::float3[]>& 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<float> 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 */
|
||||
124
ios/include/ibl/CubemapUtils.h
Normal file
124
ios/include/ibl/CubemapUtils.h
Normal file
@@ -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 <ibl/Cubemap.h>
|
||||
#include <ibl/Image.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
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<typename STATE>
|
||||
using ScanlineProc = std::function<
|
||||
void(STATE& state, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t width)>;
|
||||
|
||||
template<typename STATE>
|
||||
using ReduceProc = std::function<void(STATE& state)>;
|
||||
|
||||
//! process the cubemap using multithreading
|
||||
template<typename STATE>
|
||||
static void process(Cubemap& cm,
|
||||
utils::JobSystem& js,
|
||||
ScanlineProc<STATE> proc,
|
||||
ReduceProc<STATE> reduce = [](STATE&) {},
|
||||
const STATE& prototype = STATE());
|
||||
|
||||
//! process the cubemap
|
||||
template<typename STATE>
|
||||
static void processSingleThreaded(Cubemap& cm,
|
||||
utils::JobSystem& js,
|
||||
ScanlineProc<STATE> proc,
|
||||
ReduceProc<STATE> 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 */
|
||||
77
ios/include/ibl/Image.h
Normal file
77
ios/include/ibl/Image.h
Normal file
@@ -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 <math/scalar.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
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<uint8_t[]> detach() { return std::move(mOwnedData); }
|
||||
|
||||
private:
|
||||
size_t mBpr = 0;
|
||||
size_t mWidth = 0;
|
||||
size_t mHeight = 0;
|
||||
std::unique_ptr<uint8_t[]> mOwnedData;
|
||||
void* mData = nullptr;
|
||||
};
|
||||
|
||||
inline void* Image::getPixelRef(size_t x, size_t y) const {
|
||||
return static_cast<uint8_t*>(mData) + y * getBytesPerRow() + x * getBytesPerPixel();
|
||||
}
|
||||
|
||||
} // namespace ibl
|
||||
} // namespace filament
|
||||
|
||||
#endif /* IBL_IMAGE_H */
|
||||
57
ios/include/ibl/utilities.h
Normal file
57
ios/include/ibl/utilities.h
Normal file
@@ -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 <math.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
namespace filament {
|
||||
namespace ibl {
|
||||
|
||||
template<typename T>
|
||||
static inline constexpr T sq(T x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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 */
|
||||
@@ -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 <image/LinearImage.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <math/vec2.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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<uint8_t[]> 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_ */
|
||||
@@ -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 <imageio/ImageDecoder.h>
|
||||
|
||||
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_ */
|
||||
@@ -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 <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include <image/LinearImage.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
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_ */
|
||||
@@ -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 <image/LinearImage.h>
|
||||
|
||||
#include <utils/Path.h>
|
||||
|
||||
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
|
||||
@@ -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 <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include <image/LinearImage.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
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_ */
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <math/quat.h>
|
||||
#include <math/TVecHelpers.h>
|
||||
|
||||
#include <algorithm> // for std::swap
|
||||
#include <algorithm> // for std::swap and std::min
|
||||
#include <cmath> // for std:: namespace
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -21,14 +21,10 @@
|
||||
|
||||
#include <cmath> // for std:: namespace
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace filament {
|
||||
namespace math {
|
||||
namespace details {
|
||||
// -------------------------------------------------------------------------------------
|
||||
namespace filament::math::details {
|
||||
|
||||
template<typename U>
|
||||
inline constexpr U min(U a, U b) noexcept {
|
||||
@@ -281,8 +277,6 @@ private:
|
||||
template<typename U>
|
||||
friend inline constexpr
|
||||
bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<U>& 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
|
||||
|
||||
@@ -126,6 +126,44 @@ public:
|
||||
constexpr static TQuaternion MATH_PURE fromAxisAngle(const TVec3<A>& 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<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
|
||||
constexpr static TQuaternion MATH_PURE fromDirectedRotation(const TVec3<A>& from, const TVec3<B>& to) {
|
||||
// see the implementation of glm/gtx/quaternion.hpp
|
||||
T cosTheta = dot(from, to);
|
||||
TVec3<T> rotationAxis;
|
||||
|
||||
if (cosTheta >= T(1) - std::numeric_limits<T>::epsilon()) {
|
||||
// orig and dest point in the same direction
|
||||
return TQuaternion(1, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (cosTheta < T(-1) + std::numeric_limits<T>::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<T>(0, 0, 1), from);
|
||||
|
||||
if (length2(rotationAxis) < std::numeric_limits<T>::epsilon()) {
|
||||
// bad luck, they were parallel, try again!
|
||||
rotationAxis = cross(TVec3<T>(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
|
||||
|
||||
60
ios/include/mathio/ostream.h
Normal file
60
ios/include/mathio/ostream.h
Normal file
@@ -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 <iosfwd>
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#if __has_attribute(visibility)
|
||||
# define MATHIO_PUBLIC __attribute__((visibility("default")))
|
||||
#else
|
||||
# define MATHIO_PUBLIC
|
||||
#endif
|
||||
|
||||
namespace filament {
|
||||
namespace math {
|
||||
|
||||
namespace details { template<typename T> class TQuaternion; }
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TVec2<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TVec3<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TVec4<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TMat22<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TMat33<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TMat44<T>& v) noexcept;
|
||||
|
||||
template<typename T>
|
||||
MATHIO_PUBLIC
|
||||
std::ostream& operator<<(std::ostream& out, const details::TQuaternion<T>& v) noexcept;
|
||||
|
||||
} // namespace math
|
||||
} // namespace filament
|
||||
@@ -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 <camutils/Bookmark.h>
|
||||
#include <camutils/Manipulator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
namespace filament {
|
||||
namespace camutils {
|
||||
|
||||
template <typename FLOAT>
|
||||
Bookmark<FLOAT> Bookmark<FLOAT>::interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t) {
|
||||
Bookmark<FLOAT> result;
|
||||
using float3 = filament::math::vec3<FLOAT>;
|
||||
|
||||
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<FLOAT> 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 <typename FLOAT>
|
||||
double Bookmark<FLOAT>::duration(Bookmark<FLOAT> a, Bookmark<FLOAT> 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<float>;
|
||||
|
||||
} // namespace camutils
|
||||
} // namespace filament
|
||||
@@ -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 <camutils/Manipulator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <math/mat3.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/quat.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace filament {
|
||||
namespace camutils {
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
template<typename FLOAT>
|
||||
class FreeFlightManipulator : public Manipulator<FLOAT> {
|
||||
public:
|
||||
using vec2 = filament::math::vec2<FLOAT>;
|
||||
using vec3 = filament::math::vec3<FLOAT>;
|
||||
using vec4 = filament::math::vec4<FLOAT>;
|
||||
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||
using Base = Manipulator<FLOAT>;
|
||||
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 */
|
||||
@@ -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 <camutils/Manipulator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include "FreeFlightManipulator.h"
|
||||
#include "MapManipulator.h"
|
||||
#include "OrbitManipulator.h"
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
namespace filament {
|
||||
namespace camutils {
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::viewport(int width, int height) {
|
||||
details.viewport[0] = width;
|
||||
details.viewport[1] = height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||
details.targetPosition = {x, y, z};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) {
|
||||
details.upVector = {x, y, z};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::zoomSpeed(FLOAT val) {
|
||||
details.zoomSpeed = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||
details.orbitHomePosition = {x, y, z};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitSpeed(FLOAT x, FLOAT y) {
|
||||
details.orbitSpeed = {x, y};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDirection(Fov fov) {
|
||||
details.fovDirection = fov;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDegrees(FLOAT degrees) {
|
||||
details.fovDegrees = degrees;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::farPlane(FLOAT distance) {
|
||||
details.farPlane = distance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) {
|
||||
details.mapExtent = {worldWidth, worldHeight};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapMinDistance(FLOAT mindist) {
|
||||
details.mapMinDistance = mindist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) {
|
||||
details.flightStartPosition = {x, y, z};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) {
|
||||
details.flightStartPitch = pitch;
|
||||
details.flightStartYaw = yaw;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) {
|
||||
details.flightMaxSpeed = maxSpeed;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightSpeedSteps(int steps) {
|
||||
details.flightSpeedSteps = steps;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightPanSpeed(FLOAT x, FLOAT y) {
|
||||
details.flightPanSpeed = {x, y};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMoveDamping(FLOAT damping) {
|
||||
details.flightMoveDamping = damping;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) {
|
||||
details.groundPlane = {a, b, c, d};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT> typename
|
||||
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::raycastCallback(RayCallback cb, void* userdata) {
|
||||
details.raycastCallback = cb;
|
||||
details.raycastUserdata = userdata;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename FLOAT>
|
||||
Manipulator<FLOAT>* Manipulator<FLOAT>::Builder::build(Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::FREE_FLIGHT:
|
||||
return new FreeFlightManipulator<FLOAT>(mode, details);
|
||||
case Mode::MAP:
|
||||
return new MapManipulator<FLOAT>(mode, details);
|
||||
case Mode::ORBIT:
|
||||
return new OrbitManipulator<FLOAT>(mode, details);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FLOAT>
|
||||
Manipulator<FLOAT>::Manipulator(Mode mode, const Config& props) : mMode(mode) {
|
||||
setProperties(props);
|
||||
}
|
||||
|
||||
template <typename FLOAT>
|
||||
void Manipulator<FLOAT>::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 <typename FLOAT>
|
||||
void Manipulator<FLOAT>::setViewport(int width, int height) {
|
||||
Config props = mProps;
|
||||
props.viewport[0] = width;
|
||||
props.viewport[1] = height;
|
||||
setProperties(props);
|
||||
}
|
||||
|
||||
template <typename FLOAT>
|
||||
void Manipulator<FLOAT>::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<typename FLOAT>
|
||||
static bool raycastPlane(const filament::math::vec3<FLOAT>& origin,
|
||||
const filament::math::vec3<FLOAT>& dir, FLOAT* t, void* userdata) {
|
||||
using vec3 = filament::math::vec3<FLOAT>;
|
||||
using vec4 = filament::math::vec4<FLOAT>;
|
||||
auto props = (const typename Manipulator<FLOAT>::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 <typename FLOAT>
|
||||
void Manipulator<FLOAT>::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 <typename FLOAT>
|
||||
bool Manipulator<FLOAT>::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<FLOAT>;
|
||||
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 <typename FLOAT>
|
||||
filament::math::vec3<FLOAT> Manipulator<FLOAT>::raycastFarPlane(int x, int y) const {
|
||||
const filament::math::vec3<FLOAT> 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 <typename FLOAT>
|
||||
void Manipulator<FLOAT>::keyDown(Manipulator<FLOAT>::Key key) { }
|
||||
|
||||
template <typename FLOAT>
|
||||
void Manipulator<FLOAT>::keyUp(Manipulator<FLOAT>::Key key) { }
|
||||
|
||||
template <typename FLOAT>
|
||||
void Manipulator<FLOAT>::update(FLOAT deltaTime) { }
|
||||
|
||||
template class Manipulator<float>;
|
||||
|
||||
} // namespace camutils
|
||||
} // namespace filament
|
||||
@@ -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 <camutils/Manipulator.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
namespace filament {
|
||||
namespace camutils {
|
||||
|
||||
template<typename FLOAT>
|
||||
class MapManipulator : public Manipulator<FLOAT> {
|
||||
public:
|
||||
using vec2 = math::vec2<FLOAT>;
|
||||
using vec3 = math::vec3<FLOAT>;
|
||||
using vec4 = math::vec4<FLOAT>;
|
||||
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||
using Base = Manipulator<FLOAT>;
|
||||
using Config = typename Manipulator<FLOAT>::Config;
|
||||
|
||||
MapManipulator(Mode mode, const Config& props) : Manipulator<FLOAT>(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 */
|
||||
@@ -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 <camutils/Manipulator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
|
||||
#define MAX_PHI (F_PI / 2.0 - 0.001)
|
||||
|
||||
namespace filament {
|
||||
namespace camutils {
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
template<typename FLOAT>
|
||||
class OrbitManipulator : public Manipulator<FLOAT> {
|
||||
public:
|
||||
using vec2 = filament::math::vec2<FLOAT>;
|
||||
using vec3 = filament::math::vec3<FLOAT>;
|
||||
using vec4 = filament::math::vec4<FLOAT>;
|
||||
using Bookmark = filament::camutils::Bookmark<FLOAT>;
|
||||
using Base = Manipulator<FLOAT>;
|
||||
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 */
|
||||
@@ -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 <camutils/Bookmark.h>
|
||||
#include <camutils/Manipulator.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
namespace camutils = filament::camutils;
|
||||
|
||||
using CamManipulator = camutils::Manipulator<float>;
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
# define THROW(_e, _m) throw _e(_m)
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# 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<std::size_t GrowthFactor>
|
||||
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<std::size_t>::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 GrowthFactor = std::ratio<3, 2>>
|
||||
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<std::size_t>::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<std::size_t, 40> 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<unsigned int IPrime>
|
||||
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<std::size_t(*)(std::size_t), 40> 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<unsigned int>(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<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,863 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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<IndexSizeT>::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 CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
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<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, 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<std::pair<const CharT*, T>> 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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_map& operator=(std::initializer_list<std::pair<const CharT*, T>> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
|
||||
return m_ht.emplace(key, key_size, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string_view<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#else
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const CharT* key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, std::char_traits<CharT>::length(key), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#endif
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, key_size, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.emplace(key, key_size, std::forward<Args>(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<CharT>& 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<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
T& at(const CharT* key) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const T& at(const CharT* key) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
T& at(const std::basic_string<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::length(key)); }
|
||||
T& operator[](const std::basic_string<CharT>& 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<CharT>& 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<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type count(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& 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<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<iterator, iterator> 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<const_iterator, const_iterator> 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<typename U> 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& 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<CharT, T, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_map = array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
@@ -1,664 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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<IndexSizeT>::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 CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
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<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> 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<const CharT*> 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<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_set& operator=(std::initializer_list<const CharT*> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::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<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> 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<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& 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<iterator, bool> 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<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type erase(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& 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<CharT>::length(key)); }
|
||||
size_type count(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& 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<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<iterator, iterator> 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<const_iterator, const_iterator> 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<typename U> 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<CharT, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_set = array_set<CharT, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,647 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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 CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeySizeT = std::uint16_t>
|
||||
class htrie_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, T, Hash, KeySizeT>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> init,
|
||||
const Hash& hash = Hash()): htrie_map(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
htrie_map(std::initializer_list<std::pair<const CharT*, T>> init,
|
||||
const Hash& hash = Hash()): htrie_map(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
htrie_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
htrie_map& operator=(std::initializer_list<std::pair<const CharT*, T>> 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<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.insert(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.insert(key, std::strlen(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.insert(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.insert(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.insert(key, std::strlen(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.insert(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.insert(key, key_size, std::forward<Args>(args)...);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.insert(key, std::strlen(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.insert(key.data(), key.size(), std::forward<Args>(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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) { return m_ht.at(key.data(), key.size()); }
|
||||
const T& at(const std::basic_string_view<CharT>& 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<CharT>& key) { return m_ht.at(key.data(), key.size()); }
|
||||
const T& at(const std::basic_string<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
T& operator[](const std::basic_string_view<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<char, int> 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& value) {
|
||||
insert(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
||||
@@ -1,586 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "htrie_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a hat-trie set.
|
||||
*
|
||||
* The size of a key string is limited to std::numeric_limits<KeySizeT>::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 CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeySizeT = std::uint16_t>
|
||||
class htrie_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, void, Hash, KeySizeT>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> init,
|
||||
const Hash& hash = Hash()): htrie_set(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
htrie_set(std::initializer_list<const CharT*> init,
|
||||
const Hash& hash = Hash()): htrie_set(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
htrie_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
htrie_set& operator=(std::initializer_list<const CharT*> 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<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.insert(key, key_size);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.insert(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.insert(key, key_size);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.insert(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<char> 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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
|
||||
@@ -1,307 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
# define THROW(_e, _m) throw _e(_m)
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# 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<std::size_t GrowthFactor>
|
||||
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<std::size_t>::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 GrowthFactor = std::ratio<3, 2>>
|
||||
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<std::size_t>::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<std::size_t, 40> 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<unsigned int IPrime>
|
||||
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<std::size_t(*)(std::size_t), 40> 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<unsigned int>(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<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,863 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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<IndexSizeT>::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 CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
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<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, 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<std::pair<const CharT*, T>> 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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_map& operator=(std::initializer_list<std::pair<const CharT*, T>> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
|
||||
return m_ht.emplace(key, key_size, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string_view<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#else
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const CharT* key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, std::char_traits<CharT>::length(key), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#endif
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, key_size, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.emplace(key, key_size, std::forward<Args>(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<CharT>& 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<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
T& at(const CharT* key) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const T& at(const CharT* key) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
T& at(const std::basic_string<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::length(key)); }
|
||||
T& operator[](const std::basic_string<CharT>& 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<CharT>& 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<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type count(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& 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<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<iterator, iterator> 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<const_iterator, const_iterator> 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<typename U> 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& 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<CharT, T, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_map = array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
@@ -1,664 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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<IndexSizeT>::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 CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
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<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> 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<const CharT*> 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<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_set& operator=(std::initializer_list<const CharT*> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::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<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> 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<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& 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<iterator, bool> 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<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type erase(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& 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<CharT>::length(key)); }
|
||||
size_type count(const std::basic_string<CharT>& 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<CharT>& 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<CharT>::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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>::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<CharT>::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<CharT>& 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<CharT>& 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& 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<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& 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<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<iterator, iterator> 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<const_iterator, const_iterator> 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<typename U> 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<CharT, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_set = array_set<CharT, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,647 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#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<KeySizeT>::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 CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeySizeT = std::uint16_t>
|
||||
class htrie_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, T, Hash, KeySizeT>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> init,
|
||||
const Hash& hash = Hash()): htrie_map(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
htrie_map(std::initializer_list<std::pair<const CharT*, T>> init,
|
||||
const Hash& hash = Hash()): htrie_map(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
htrie_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
htrie_map& operator=(std::initializer_list<std::pair<const CharT*, T>> 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<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.insert(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.insert(key, std::strlen(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.insert(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> 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<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.insert(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.insert(key, std::strlen(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.insert(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.insert(key, key_size, std::forward<Args>(args)...);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.insert(key, std::strlen(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.insert(key.data(), key.size(), std::forward<Args>(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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) { return m_ht.at(key.data(), key.size()); }
|
||||
const T& at(const std::basic_string_view<CharT>& 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<CharT>& key) { return m_ht.at(key.data(), key.size()); }
|
||||
const T& at(const std::basic_string<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
T& operator[](const std::basic_string_view<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<char, int> 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& value) {
|
||||
insert(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
||||
@@ -1,586 +0,0 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* 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 <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "htrie_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a hat-trie set.
|
||||
*
|
||||
* The size of a key string is limited to std::numeric_limits<KeySizeT>::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 CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeySizeT = std::uint16_t>
|
||||
class htrie_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, void, Hash, KeySizeT>;
|
||||
|
||||
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<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> init,
|
||||
const Hash& hash = Hash()): htrie_set(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
htrie_set(std::initializer_list<const CharT*> init,
|
||||
const Hash& hash = Hash()): htrie_set(hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
htrie_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
htrie_set& operator=(std::initializer_list<const CharT*> 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<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.insert(key, key_size);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.insert(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::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<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.insert(key, key_size);
|
||||
}
|
||||
#ifdef TSL_HT_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.insert(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.insert(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& 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<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::strlen(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& 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<prefix_iterator, prefix_iterator> 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<const_prefix_iterator, const_prefix_iterator> 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<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& 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<char> 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<CharT>& 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<CharT>& 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<CharT>& 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<CharT>& 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<class Serializer>
|
||||
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<typename U> 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<class Deserializer>
|
||||
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
|
||||
@@ -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 <utils/compiler.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
class BinaryTreeArray {
|
||||
|
||||
// Simple fixed capacity stack
|
||||
template<typename TYPE, size_t CAPACITY,
|
||||
typename = typename std::enable_if<std::is_pod<TYPE>::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<typename Leaf, typename Node>
|
||||
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<TNode, 16> 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
|
||||
@@ -86,7 +86,9 @@ public:
|
||||
constexpr StaticString(StringLiteral<N> 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<N> 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<size_t N>
|
||||
static constexpr size_type computeHash(StringLiteral<N> 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<size_t N>
|
||||
explicit CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
|
||||
CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
|
||||
: CString(other, N - 1) {
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
/** Demangles a C++ type name */
|
||||
static utils::CString demangleTypeName(const char* mangled);
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
static utils::CString typeName() {
|
||||
#if UTILS_HAS_RTTI
|
||||
return demangleTypeName(typeid(T).name());
|
||||
|
||||
@@ -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 <utils/linux/Condition.h>
|
||||
#else
|
||||
#include <utils/generic/Condition.h>
|
||||
#endif
|
||||
|
||||
#endif // TNT_UTILS_CONDITION_H
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
// note: we use our version of mutex/condition to keep this public header STL free
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
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
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
// note: we use our version of mutex/condition to keep this public header STL free
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
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
|
||||
@@ -20,12 +20,11 @@
|
||||
#include <utils/compressed_pair.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <vector> // TODO: is this necessary?
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 <functional> // for std::hash
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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<typename T>
|
||||
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<class T>
|
||||
inline void combine(size_t& seed, const T& v) noexcept {
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
|
||||
}
|
||||
|
||||
// combines two hashes together, faster but less good
|
||||
template<class T>
|
||||
inline void combine_fast(size_t& seed, const T& v) noexcept {
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) << 1u;
|
||||
}
|
||||
|
||||
} // namespace hash
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_HASH_H
|
||||
155
ios/include/utils/Invocable.h
Normal file
155
ios/include/utils/Invocable.h
Normal file
@@ -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 <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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<typename Fn, typename R, typename... Args>
|
||||
using EnableIfFnMatchesInvocable =
|
||||
std::enable_if_t<std::is_invocable_v<Fn, Args...> &&
|
||||
std::is_same_v<R, std::invoke_result_t<Fn, Args...>>, int>;
|
||||
#else
|
||||
template<typename Fn, typename R, typename... Args>
|
||||
using EnableIfFnMatchesInvocable = std::enable_if_t<true, int>;
|
||||
#endif
|
||||
|
||||
template<typename Signature>
|
||||
class Invocable;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class Invocable<R(Args...)> {
|
||||
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<typename Fn, EnableIfFnMatchesInvocable<Fn, R, Args...> = 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<typename... OperatorArgs>
|
||||
R operator()(OperatorArgs&& ... args);
|
||||
template<typename... OperatorArgs>
|
||||
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<typename R, typename... Args>
|
||||
template<typename Fn, EnableIfFnMatchesInvocable<Fn, R, Args...>>
|
||||
Invocable<R(Args...)>::Invocable(Fn&& fn) noexcept
|
||||
: mInvocable(new Fn(std::forward<std::decay_t<Fn>>(fn))),
|
||||
mDeleter(+[](void* erased_invocable) {
|
||||
auto typed_invocable = static_cast<Fn*>(erased_invocable);
|
||||
delete typed_invocable;
|
||||
}),
|
||||
mInvoker(+[](void* erased_invocable, Args... args) -> R {
|
||||
auto typed_invocable = static_cast<Fn*>(erased_invocable);
|
||||
return (*typed_invocable)(std::forward<Args>(args)...);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::~Invocable() noexcept {
|
||||
if (mDeleter) {
|
||||
mDeleter(mInvocable);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::Invocable(Invocable&& rhs) noexcept
|
||||
: mInvocable(rhs.mInvocable),
|
||||
mDeleter(rhs.mDeleter),
|
||||
mInvoker(rhs.mInvoker) {
|
||||
rhs.mInvocable = nullptr;
|
||||
rhs.mDeleter = nullptr;
|
||||
rhs.mInvoker = nullptr;
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>& Invocable<R(Args...)>::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<typename R, typename... Args>
|
||||
template<typename... OperatorArgs>
|
||||
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) {
|
||||
assert(mInvoker && mInvocable);
|
||||
return mInvoker(mInvocable, std::forward<OperatorArgs>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
template<typename... OperatorArgs>
|
||||
R Invocable<R(Args...)>::operator()(OperatorArgs&& ... args) const {
|
||||
assert(mInvoker && mInvocable);
|
||||
return mInvoker(mInvocable, std::forward<OperatorArgs>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
Invocable<R(Args...)>::operator bool() const noexcept {
|
||||
return mInvoker != nullptr && mInvocable != nullptr;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_INVOKABLE_H
|
||||
@@ -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 <assert.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/memalign.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/WorkStealingDequeue.h>
|
||||
|
||||
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<uint16_t, MAX_JOB_COUNT>;
|
||||
|
||||
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<void()>) > 48 ? sizeof(std::function<void()>) : 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<uint16_t> runningJobCount = { 1 }; // 2 | 2
|
||||
mutable std::atomic<uint16_t> 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<Foo, &Foo::method>(parent, &foo)
|
||||
* createJob<Foo, &Foo::method>(parent, foo)
|
||||
* createJob<Foo, &Foo::method>(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<typename T, void(T::*method)(JobSystem&, Job*)>
|
||||
Job* createJob(Job* parent, T* data) noexcept {
|
||||
Job* job = create(parent, [](void* user, JobSystem& js, Job* job) {
|
||||
(*static_cast<T**>(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<typename T, void(T::*method)(JobSystem&, Job*)>
|
||||
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<T*>(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<typename T>
|
||||
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<T*>(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<Mutex>& 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<uint32_t> mActiveJobs = { 0 };
|
||||
utils::Arena<utils::ThreadSafeObjectPoolAllocator<Job>, LockingPolicy::NoLock> mJobPool;
|
||||
|
||||
template <typename T>
|
||||
using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
|
||||
|
||||
// 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<ThreadState> mThreadStates; // actual data is stored offline
|
||||
std::atomic<bool> mExitRequested = { false }; // this one is almost never written
|
||||
std::atomic<uint16_t> 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<std::thread::id, ThreadState *> 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<typename CALLABLE, typename ... ARGS>
|
||||
JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent,
|
||||
CALLABLE&& func, ARGS&&... args) noexcept {
|
||||
struct Data {
|
||||
std::function<void()> f;
|
||||
// Renaming the method below could cause an Arrested Development.
|
||||
void gob(JobSystem&, JobSystem::Job*) noexcept { f(); }
|
||||
} user{ std::bind(std::forward<CALLABLE>(func),
|
||||
std::forward<ARGS>(args)...) };
|
||||
return js.createJob<Data, &Data::gob>(parent, std::move(user));
|
||||
}
|
||||
|
||||
template<typename CALLABLE, typename T, typename ... ARGS,
|
||||
typename = typename std::enable_if<
|
||||
std::is_member_function_pointer<typename std::remove_reference<CALLABLE>::type>::value
|
||||
>::type
|
||||
>
|
||||
JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent,
|
||||
CALLABLE&& func, T&& o, ARGS&&... args) noexcept {
|
||||
struct Data {
|
||||
std::function<void()> f;
|
||||
// Renaming the method below could cause an Arrested Development.
|
||||
void gob(JobSystem&, JobSystem::Job*) noexcept { f(); }
|
||||
} user{ std::bind(std::forward<CALLABLE>(func), std::forward<T>(o),
|
||||
std::forward<ARGS>(args)...) };
|
||||
return js.createJob<Data, &Data::gob>(parent, std::move(user));
|
||||
}
|
||||
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename S, typename F>
|
||||
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<JobData, &JobData::parallelWithJobs>(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<typename S, typename F>
|
||||
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<S, F>;
|
||||
JobData jobData(start, count, 0, std::move(functor), splitter);
|
||||
return js.createJob<JobData, &JobData::parallelWithJobs>(parent, std::move(jobData));
|
||||
}
|
||||
|
||||
// parallel jobs with pointer/count
|
||||
template<typename T, typename S, typename F>
|
||||
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<S, decltype(user)>;
|
||||
JobData jobData(0, count, 0, std::move(user), splitter);
|
||||
return js.createJob<JobData, &JobData::parallelWithJobs>(parent, std::move(jobData));
|
||||
}
|
||||
|
||||
// parallel jobs on a Slice<>
|
||||
template<typename T, typename S, typename F>
|
||||
JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent,
|
||||
utils::Slice<T> slice, F functor, const S& splitter) noexcept {
|
||||
return parallel_for(js, parent, slice.data(), slice.size(), functor, splitter);
|
||||
}
|
||||
|
||||
|
||||
template <size_t COUNT, size_t MAX_SPLITS = 12>
|
||||
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
|
||||
@@ -424,8 +424,10 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic<ArithmeticPanic> {
|
||||
|
||||
#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<ArithmeticPanic> {
|
||||
* @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<ArithmeticPanic> {
|
||||
* @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<ArithmeticPanic> {
|
||||
* @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<ArithmeticPanic> {
|
||||
* @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__)
|
||||
|
||||
/**
|
||||
|
||||
66
ios/include/utils/PrivateImplementation-impl.h
Normal file
66
ios/include/utils/PrivateImplementation-impl.h
Normal file
@@ -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 <utils/PrivateImplementation.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::PrivateImplementation() noexcept
|
||||
: mImpl(new T) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename ... ARGS>
|
||||
PrivateImplementation<T>::PrivateImplementation(ARGS&& ... args) noexcept
|
||||
: mImpl(new T(std::forward<ARGS>(args)...)) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::~PrivateImplementation() noexcept {
|
||||
delete mImpl;
|
||||
}
|
||||
|
||||
#ifndef UTILS_PRIVATE_IMPLEMENTATION_NON_COPYABLE
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>::PrivateImplementation(PrivateImplementation const& rhs) noexcept
|
||||
: mImpl(new T(*rhs.mImpl)) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PrivateImplementation<T>& PrivateImplementation<T>::operator=(PrivateImplementation<T> const& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
*mImpl = *rhs.mImpl;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // UTILS_PRIVATEIMPLEMENTATION_IMPL_H
|
||||
61
ios/include/utils/PrivateImplementation.h
Normal file
61
ios/include/utils/PrivateImplementation.h
Normal file
@@ -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 <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
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<typename T>
|
||||
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<typename ... ARGS>
|
||||
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
|
||||
@@ -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 <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <chrono> // note: This is safe (only used inline)
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <unistd.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <linux/perf_event.h>
|
||||
#endif
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
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<uint64_t, std::nano> getWallTime() const {
|
||||
return std::chrono::duration<uint64_t, std::nano>(time_enabled);
|
||||
}
|
||||
|
||||
std::chrono::duration<uint64_t, std::nano> getRunningTime() const {
|
||||
return std::chrono::duration<uint64_t, std::nano>(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
|
||||
@@ -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 <algorithm>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#ifndef TNT_UTILS_SLICE_H
|
||||
#define TNT_UTILS_SLICE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user