upgrade to Filament 1.21.0

This commit is contained in:
Nick Fisher
2022-04-14 01:54:33 +08:00
parent 6d271e2de3
commit 629b81968d
185 changed files with 4458 additions and 20211 deletions

View File

@@ -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(

View File

@@ -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
);
}

View File

@@ -20,8 +20,6 @@ interface FilamentInterop : Library {
fun filament_viewer_new(
layer:Object,
opaqueShaderPath:String,
fadeShaderPath:String,
env:JNIEnv,
am:AssetManager
) : Pointer;

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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{};
};

View 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

View 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

View 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

View 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

View 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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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;
};

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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&)>

View 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

View 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 Lengyels 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

View 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

View File

@@ -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;
};

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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>

View File

@@ -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
View 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

View 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

View 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
View 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 */

View 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
View 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 */

View 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
View 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 */

View 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 */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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

View File

@@ -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_ */

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {
}

View File

@@ -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());

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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; }
};

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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__)
/**

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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