1. 优化内存池

2. 延迟补偿功能完成
3. 优化内存布局
This commit is contained in:
Nanako 2024-07-12 07:55:12 +08:00
parent c4abac61db
commit e65c44899a
40 changed files with 618 additions and 2845 deletions

View File

@ -36,6 +36,5 @@ set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(core)
add_subdirectory(third_party/rtaudio)
add_subdirectory(third_party/spdlog)
add_subdirectory(third_party/mempool)
add_subdirectory(third_party/taskflow)
add_subdirectory(third_party/glfw)

View File

@ -1,15 +1,15 @@
function(retrieve_files path out_files)
function(retrieve_files_custom path extension out_files)
message(STATUS "Retrieving files in ${path}")
set(EXTENSIONS "")
foreach(ext ${extension})
list(APPEND EXTENSIONS "${path}/*.${ext}")
endforeach ()
# .h .hpp. ini HEAD_FILES
file(GLOB_RECURSE HEAD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${path}/*.h ${path}/*.hpp ${path}/*.ini)
# *.cpp *.c SRC_FILES
file(GLOB_RECURSE SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${path}/*.cpp ${path}*.c ${path}*.ixx)
file(GLOB_RECURSE FIND_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${EXTENSIONS})
# HEDADER_FILES SRC_FILES ALL_FILES
set(ALL_FILES ${HEAD_FILES} ${SRC_FILES})
set(ALL_FILES ${FIND_FILES})
set(RESULT "")
@ -53,3 +53,9 @@ function(retrieve_files path out_files)
set(${out_files} ${RESULT} PARENT_SCOPE)
endfunction()
function(retrieve_files path out_files)
set(temp_files "")
retrieve_files_custom(${path} "h;hpp;ini;cpp;c;ixx" temp_files)
set(${out_files} ${temp_files} PARENT_SCOPE)
endfunction()

View File

@ -10,7 +10,7 @@ add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow glfw)
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog Taskflow glfw)
option(USE_DOUBLE_SAMPLE "Use double sample" OFF)
add_definitions(-Dcore_EXPORTS)
@ -27,27 +27,27 @@ endif ()
# platform micros
if (WIN32)
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=1 PLATFORM_MACOS=0 PLATFORM_LINUX=0 GLFW_EXPOSE_NATIVE_WIN32)
elseif(APPLE)
elseif (APPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=0 PLATFORM_MACOS=1 PLATFORM_LINUX=0 GLFW_EXPOSE_NATIVE_COCOA)
elseif(UNIX AND NOT APPLE)
elseif (UNIX AND NOT APPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=0 PLATFORM_MACOS=0 PLATFORM_LINUX=1 GLFW_EXPOSE_NATIVE_X11)
endif()
endif ()
# cpu amd or arm
if (CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
target_compile_definitions(${PROJECT_NAME} PUBLIC CPU_AMD64=1 CPU_ARM=0)
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|ARM.*")
target_compile_definitions(${PROJECT_NAME} PUBLIC CPU_AMD64=0 CPU_ARM=1)
endif()
endif ()
if (CMAKE_BUILD_TYPE MATCHES "Debug")
target_compile_definitions(${PROJECT_NAME} PUBLIC BUILD_DEBUG=1)
else()
else ()
target_compile_definitions(${PROJECT_NAME} PUBLIC BUILD_DEBUG=0)
endif()
endif ()
if (USE_DOUBLE_SAMPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_DOUBLE_SAMPLE=1)
else()
else ()
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_DOUBLE_SAMPLE=0)
endif ()

View File

@ -27,9 +27,7 @@ public:
bool update_application();
virtual void tick(float delta_time)
{
}
virtual void tick(float delta_time) {}
protected:
};

View File

@ -24,8 +24,8 @@ void audio_device_manager::init(singleton_initliazer& initliazer) {
singleton_t<audio_device_manager>::init(initliazer);
audio_ = new RtAudio();
log_all_devices();
output_params_.deviceId = audio_->getDefaultOutputDevice();
// output_params_.deviceId = 131;
// output_params_.deviceId = audio_->getDefaultOutputDevice();
output_params_.deviceId = 131;
output_params_.nChannels = get_output_channel_count();
output_params_.firstChannel = 0;
buffer_size_ = 512;

View File

@ -155,7 +155,7 @@ std::vector<sample_t> audio_buffer::get_interleaved_buffer() const {
void audio_buffer::free() {
for (sample_t* header : headers_)
get_audio_buffer_pool()->free(header);
get_audio_buffer_pool()->free(header, frame_size_);
headers_.clear();
}

View File

@ -7,15 +7,18 @@
class CORE_API audio_buffer {
public:
static void(*add_func)(audio_buffer& in_buffer, audio_buffer& from_buffer, float percent);
static void(*multiple_func)(audio_buffer& in_buffer, float percent);
static void (*add_func)(audio_buffer& in_buffer, audio_buffer& from_buffer, float percent);
static void (*multiple_func)(audio_buffer& in_buffer, float percent);
audio_buffer();
audio_buffer(audio_buffer&& in_buffer) noexcept {
headers_ = std::move(in_buffer.headers_);
frame_size_ = in_buffer.frame_size_;
in_buffer.frame_size_ = 0;
}
audio_buffer& operator=(audio_buffer&& in_buffer) noexcept {
if (this != &in_buffer) {
headers_ = std::move(in_buffer.headers_);
@ -24,23 +27,28 @@ public:
}
return *this;
}
~audio_buffer();
sample_t** get_headers() { return headers_.data(); }
const std::vector<sample_t*>& get_headers_vector() { return headers_; }
[[nodiscard]] auto get_headers() -> sample_t** { return headers_.data(); }
[[nodiscard]] auto get_headers_vector() const -> const std::vector<sample_t*>& { return headers_; }
[[nodiscard]] uint32_t get_num_channels() const { return headers_.size(); }
[[nodiscard]] uint32_t get_num_samples() const { return frame_size_; }
[[nodiscard]] auto get_num_channels() const -> uint32_t { return headers_.size(); }
[[nodiscard]] auto get_num_samples() const -> uint32_t { return frame_size_; }
void resize(uint32_t channel_num, uint32_t frame_size);
void clear();
void add(audio_buffer& from_buffer, float percent = 1.f);
void multiple(float percent);
[[nodiscard]] std::vector<sample_t> get_interleaved_buffer() const;
private:
void free();
private:
std::vector<sample_t*> headers_{};
std::mutex lock_{};

View File

@ -1,15 +1,26 @@
#include "audio_buffer_pool.h"
#include "audio/mixer/mixer.h"
IMPL_SINGLETON_INSTANCE(audio_buffer_pool)
void audio_buffer_pool::init(singleton_initliazer& initliazer) {
}
sample_t * audio_buffer_pool::alloc(uint32_t block_size) {
auto* alloc_block = static_cast<sample_t*>(pool_.alloc(block_size * sizeof(sample_t)));
return alloc_block;
void audio_buffer_pool::release(singleton_release_guard& release_guard) {
singleton_t<audio_buffer_pool>::release(release_guard);
for (auto& pool : pool_map_) {
delete pool.second;
}
}
void audio_buffer_pool::free(sample_t* block) {
pool_.free(block);
sample_t* audio_buffer_pool::alloc(uint32_t block_size) {
if (pool_map_.find(block_size) == pool_map_.end()) {
pool_map_.emplace(block_size, new memory_pool(sizeof(sample_t) * block_size, 256));
}
return static_cast<sample_t*>(pool_map_[block_size]->allocate());
}
void audio_buffer_pool::free(sample_t* block, uint32_t block_size) {
pool_map_[block_size]->deallocate(block);
}

View File

@ -1,18 +1,20 @@
#pragma once
#include "mempool.h"
#include <unordered_map>
#include "../../misc/mempool.h"
#include "misc/singleton/singleton.h"
class CORE_API audio_buffer_pool : public singleton_t<audio_buffer_pool> {
public:
void init(singleton_initliazer& initliazer) override;
void release(singleton_release_guard& release_guard) override;
const char* get_name() override { return "audio_buffer_pool"; }
sample_t* alloc(uint32_t block_size);
void free(sample_t* block);
void free(sample_t* block, uint32_t block_size);
protected:
[[nodiscard]] mempool<>& get_pool() { return pool_; }
private:
mempool<> pool_ = mempool(1024000 * 4); // 一个4MB的内存池, 如果一个缓冲区大小为1024个样本, 那么这个内存池可以分配1024 * 4个缓冲区
std::unordered_map<uint32_t, memory_pool*> pool_map_;
};
DEFINE_SINGLETON_INSTANCE(audio_buffer_pool)

View File

@ -14,5 +14,5 @@ mixer_channel_node::mixer_channel_node(channel_interface* in_owner, mixer_track*
}
const std::vector<sample_t*>& null_channel_node::get_channel_headers() {
return g_mixer.zero_track.get_headers_vector();
return g_mixer.get_zero_track()->get_headers_vector();
}

View File

@ -17,9 +17,11 @@ class channel_node {
public:
virtual ~channel_node() = default;
channel_node(channel_interface* in_owner, channel_node_type in_type) : owner(in_owner), type(in_type) {}
channel_node(channel_interface* in_owner, channel_node_type in_type) : owner(in_owner)
, type(in_type) {}
virtual const std::vector<sample_t*>& get_channel_headers() = 0;
virtual std::string get_name() = 0;
channel_interface* owner;
@ -34,6 +36,7 @@ public:
std::string get_name() override { return "MixerChannelNode"; }
[[nodiscard]] mixer_track* get_track() const { return track_; }
private:
mixer_track* track_;
std::vector<sample_t*> channel_headers_;
@ -42,10 +45,16 @@ private:
class null_channel_node : public channel_node {
static null_channel_node* instance;
public:
null_channel_node() : channel_node(nullptr, channel_node_type::null) {}
static void init() { instance = new null_channel_node(); }
static void destroy() { delete instance; instance = nullptr; }
static void destroy() {
delete instance;
instance = nullptr;
}
static null_channel_node* get() { return instance; }
std::string get_name() override { return "NullChannelNode"; }

View File

@ -27,7 +27,7 @@ void latency_compensator::set_latency(uint32_t in_latency) {
latency_ = in_latency;
const uint32_t block_size = g_audio_device_manager.get_buffer_size();
const int32_t block_num = in_latency / block_size + 1;
const int32_t block_num = std::ceil((float)in_latency / block_size) + 1;
resize(block_num * block_size);
push_zeros(in_latency);
}
@ -51,9 +51,9 @@ void latency_compensator::pop(uint64_t delta_sample, circular_buffer_vector_type
}
}
void latency_compensator::pop(uint64_t delta_smaple, audio_buffer& out_buffer) {
void latency_compensator::pop(audio_buffer& out_buffer) {
for (auto buffer : std::views::zip(internal_buffer, out_buffer.get_headers_vector())) {
auto& [internal, target_buffer] = buffer;
internal.pop(target_buffer, delta_smaple);
internal.pop(target_buffer, out_buffer.get_num_samples());
}
}

View File

@ -17,7 +17,7 @@ public:
void push(audio_buffer& in_buffer);
void pop(uint64_t delta_sample, circular_buffer_vector_type& out_buffer);
void pop(uint64_t delta_smaple, audio_buffer& out_buffer);
void pop(audio_buffer& out_buffer);
void set_latency(uint32_t in_latency);
[[nodiscard]] uint32_t get_latency() const { return latency_; }

View File

@ -4,10 +4,7 @@
#include "channel_node.h"
#include "audio/device/audio_device_manager.h"
#include "audio/misc/audio_buffer_pool.h"
#include "audio/plugin_host/plugin_host.h"
#include "audio/plugin_host/plugin_host_manager.h"
#include "misc/query_timer.h"
#include "misc/taskflow_singleton.h"
#include "thread_message/thread_message_hubs.h"
IMPL_SINGLETON_INSTANCE(mixer)
@ -20,12 +17,14 @@ void mixer::init(singleton_initliazer& initliazer) {
null_channel_node::init();
zero_track.rename("zero");
zero_track.init();
zero_track_ = new dummy_track();
zero_track_->rename("zero");
zero_track_->init();
auto* master = obj_mempool<dummy_track>::alloc();
master->rename("master");
push_track(master);
master_ = new dummy_track();
master_->rename("master");
master_->init();
push_track(master_);
}
void mixer::begin_release(singleton_release_guard &release_guard) {
@ -37,13 +36,15 @@ void mixer::release(singleton_release_guard& release_guard) {
singleton_t<mixer>::release(release_guard);
release_guard.require_release<audio_device_manager>();
null_channel_node::destroy();
obj_mempool<dummy_track>::free_all();
obj_mempool<instrument_track>::free_all();
for (auto track : tracks_) {
delete track;
}
delete zero_track_;
tracks_.clear();
}
dummy_track* mixer::create_dummy_track(const std::string& in_name) {
auto* track = obj_mempool<dummy_track>::alloc();
auto* track = new dummy_track();
track->init();
track->rename(in_name);
@ -55,7 +56,7 @@ dummy_track* mixer::create_dummy_track(const std::string& in_name) {
}
instrument_track* mixer::create_instrument_track(plugin_host* in_instrument) {
auto* track = obj_mempool<instrument_track>::alloc(in_instrument);
auto* track = new instrument_track(in_instrument);
track->init();
// register track
@ -78,16 +79,7 @@ void mixer::remove_track(mixer_track* track) {
on_remove_track_main_thread.broadcast(track);
if (track == selected_track)
selected_track = nullptr;
switch (track->get_type()) {
case mixer_track_type::unknown:
break;
case mixer_track_type::dummy:
obj_mempool<dummy_track>::free(dynamic_cast<dummy_track*>(track));
break;
case mixer_track_type::instrument:
obj_mempool<instrument_track>::free(dynamic_cast<instrument_track*>(track));
break;
}
delete track;
});
});
}
@ -128,5 +120,7 @@ void mixer::thread_register_track(mixer_track* track) {
}
void mixer::on_mixer_latency_changed() {
processor_.update_latency();
g_audio_thread_hub.push_message([this] {
processor_.update_latency();
});
}

View File

@ -5,7 +5,7 @@
#include <string>
#include <unordered_map>
#include "mempool.h"
#include "../../misc/mempool.h"
#include "mixer_processor.h"
#include "mixer_track.h"
#include "taskflow/taskflow.hpp"
@ -30,10 +30,10 @@ public:
void process(uint32_t in_frames, circular_buffer_vector_type& out_buffer);
void reset();
[[nodiscard]] dummy_track* get_master() const { return reinterpret_cast<dummy_track*>(tracks_[0]); }
[[nodiscard]] auto get_master() const -> dummy_track* { return master_; }
[[nodiscard]] auto get_zero_track() const -> dummy_track* { return zero_track_; }
void request_build_process_node();
dummy_track zero_track; // 用于没有任何音频输出的通道
multicast_delegate<mixer_track*> on_add_track;
multicast_delegate<mixer_track*> on_add_track_main_thread;
@ -48,6 +48,8 @@ private:
void on_mixer_latency_changed();
dummy_track* master_;
dummy_track* zero_track_; // 用于没有任何音频输出的通道
std::vector<mixer_track*> tracks_;
mixer_processor processor_;
bool process_node_dirty_ = false;

View File

@ -1,8 +1,10 @@
#include "mixer_processor.h"
#include <ranges>
#include "channel_interface.h"
#include "channel_node.h"
#include "mempool.h"
#include "../../misc/mempool.h"
#include "mixer.h"
#include "mixer_track.h"
#include "audio/plugin_host/plugin_host.h"
@ -10,12 +12,11 @@
#include "misc/taskflow_singleton.h"
struct build_process_node {
explicit build_process_node(std::unordered_map<mixer_track *, std::vector<mixer_track *>> &track_map)
: track_map(track_map) {
}
explicit build_process_node(
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>>& in_track_map) : track_map(in_track_map) {}
void run() {
build(g_mixer.get_master());
build();
build_instruments(g_plugin_host_manager.get_instrument_hosts());
for (const auto& pair : processed_tracks_) {
@ -27,103 +28,143 @@ struct build_process_node {
std::ranges::sort(layer_order, std::greater());
}
auto get_result() -> std::unordered_map<int32_t, std::vector<mixer_track*>> {
return layer_tracks;
}
auto get_order() -> std::vector<int32_t> {
return layer_order;
}
private:
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>>& track_map;
std::unordered_map<mixer_track*, int32_t> processed_tracks_;
std::unordered_map<int32_t, std::vector<mixer_track*>> layer_tracks;
std::vector<int32_t> layer_order;
std::unordered_map<mixer_track*, std::vector<mixer_track*>>& track_map;
private:
int32_t layer = 0;
std::unordered_map<mixer_track*, int32_t> processed_tracks_;
void build() {
build(g_mixer.get_master(), 0);
}
void build(mixer_track* current_track) {
void build(mixer_track* current_track, int32_t layer) {
int32_t& track_current_layer = processed_tracks_[current_track];
track_current_layer = std::max(track_current_layer, layer);
const std::vector<mixer_track*>& children = track_map[current_track];
for (mixer_track* child : children) {
build(child);
for (const plugin_host* effect : child->effects) {
build_effect_channel_interface(child, effect->channel, processed_tracks_);
const auto& child_tracks = track_map[current_track];
for (mixer_track_link* link : child_tracks) {
mixer_track* child_track = link->from();
build(child_track, layer + 1);
const auto& effects = child_track->get_effects();
for (const plugin_host* effect : effects) {
build_effect_channel_interface(child_track, effect->channel, processed_tracks_);
}
}
}
void build_effect_channel_interface(mixer_track *track, const channel_interface *in_interface,
std::unordered_map<mixer_track *, int32_t> &processed_tracks) {
int32_t &track_current_layer = processed_tracks[track];
auto &input_channel_nodes = in_interface->input_channel_nodes;
auto &output_channel_nodes = in_interface->output_channel_nodes;
void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface,
std::unordered_map<mixer_track*, int32_t>& processed_tracks) {
int32_t& track_current_layer = processed_tracks[track];
auto& input_channel_nodes = in_interface->input_channel_nodes;
auto& output_channel_nodes = in_interface->output_channel_nodes;
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
for (int i = 1; i < input_channel_nodes.size(); ++i) {
channel_node *node = input_channel_nodes[i];
if (node->type != channel_node_type::mixer)
continue;
const mixer_channel_node *mixer_node = dynamic_cast<mixer_channel_node *>(node);
auto *InputTrack = mixer_node->get_track();
int32_t &target_mixer_current_layer = processed_tracks[InputTrack];
channel_node* node = input_channel_nodes[i];
if (node->type != channel_node_type::mixer) continue;
const mixer_channel_node* mixer_node = dynamic_cast<mixer_channel_node*>(node);
auto* InputTrack = mixer_node->get_track();
int32_t& target_mixer_current_layer = processed_tracks[InputTrack];
target_mixer_current_layer = std::max(target_mixer_current_layer + 1, track_current_layer + 1);
}
// 如果这个效果器需要输出到其他轨道,那么这个轨道的深度就是目标轨道的深度+1
for (int i = 1; i < output_channel_nodes.size(); ++i) {
auto *node = output_channel_nodes[i];
if (node->type != channel_node_type::mixer)
continue;
const auto *MixerChannelNode = dynamic_cast<mixer_channel_node *>(node);
auto *MixerTrack = MixerChannelNode->get_track();
const int32_t &TargetMixerCurrentLayer = processed_tracks[MixerTrack];
auto* node = output_channel_nodes[i];
if (node->type != channel_node_type::mixer) continue;
const auto* MixerChannelNode = dynamic_cast<mixer_channel_node*>(node);
auto* MixerTrack = MixerChannelNode->get_track();
const int32_t& TargetMixerCurrentLayer = processed_tracks[MixerTrack];
track_current_layer = std::max(track_current_layer, TargetMixerCurrentLayer + 1);
}
}
void build_instruments(const std::vector<plugin_host*>& instruments) {
for (auto instrument: instruments) {
for (auto instrument : instruments) {
build_instrument_process_node(instrument, processed_tracks_);
}
}
void build_instrument_process_node(const plugin_host *host,
std::unordered_map<mixer_track *, int32_t> &processed_tracks) {
for (mixer_track *Track: host->owner_tracks) {
void build_instrument_process_node(const plugin_host* host,
std::unordered_map<mixer_track*, int32_t>& processed_tracks) {
for (mixer_track* Track : host->owner_tracks) {
build_effect_channel_interface(Track, host->channel, processed_tracks);
}
}
};
mixer_track_link::mixer_track_link() {
compensator_.init();
}
mixer_track_link::mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level): from_(from_track)
, send_to_(to)
, send_level_(in_send_level) {
compensator_.init();
}
void mixer_track_link::process() {
audio_buffer& from_buffer = from_->get_buffer();
audio_buffer& to_buffer = send_to_->get_buffer();
audio_buffer temp_mix_buffer;
temp_mix_buffer.resize(2, to_buffer.get_num_samples());
compensator_.pop(temp_mix_buffer);
to_buffer.add(temp_mix_buffer, send_level_);
compensator_.push(from_buffer);
}
void mixer_processor::process(uint32_t in_frames) {
task_frame_ = in_frames;
for (auto& flow: taskflow_) {
for (auto& flow : taskflow_) {
g_executor->run(flow).wait();
}
}
void mixer_processor::pop_master(uint32_t in_frames, circular_buffer_vector_type &out_buffer) {
g_mixer.get_master();
g_mixer.compensator_.pop(in_frames, out_buffer);
void mixer_processor::pop_master(uint32_t in_frames, circular_buffer_vector_type& out_buffer) {
const auto master = g_mixer.get_master();
const auto& buffer = master->get_buffer();
const std::vector<sample_t*>& headers = buffer.get_headers_vector();
for (const auto& pair : std::ranges::views::zip(headers, out_buffer)) {
const auto& [in, out] = pair;
out.push(in, in_frames);
}
}
void mixer_processor::update_taskflow() {
build_process_node build_node(layer_tracks_);
build_process_node build_node(track_map_);
build_node.run();
auto& layer_order = build_node.layer_order;
auto& layer_tracks = build_node.layer_tracks;
auto layer_order = build_node.get_order();
auto layer_map = build_node.get_result();
taskflow_.clear();
for (int32_t order : layer_order) {
const auto& layer = layer_tracks[order];
tf::Taskflow taskflow("mixer_update_layer" + std::to_string(order));
for (const auto& order : layer_order) {
const auto& track_group = layer_map[order];
tf::Taskflow taskflow("mixer_update_layer");
// 每层的轨道并行处理
for (mixer_track* track : layer) {
for (mixer_track* track : track_group) {
taskflow.emplace([track, this] {
auto& track_buffer_header = track;
audio_buffer& track_buffer = track->get_buffer();
std::vector<mixer_track_link*>& links = track_map_[track];
// 从children中获取buffer
for (const auto &link: children) {
link.track->compensator_.pop(in_frames, temp_mix_buffer_);
// audio_buffer& child_buffer = link.track->buffer;
buffer_.add(temp_mix_buffer_, link.send_level);
for (mixer_track_link* link : links) {
link->process();
}
track->process(task_frame_);
track->push_ui_buffer(track_buffer);
});
}
taskflow_.push_back(std::move(taskflow));
}
}
@ -131,45 +172,63 @@ void mixer_processor::update_taskflow() {
void mixer_processor::update_latency() {
int32_t max_latency = 0;
int32_t min_latency = 0;
// for (mixer_track* track : tracks_) {
// max_latency = std::max<int32_t>(max_latency, track->get_real_latency());
// min_latency = std::min<int32_t>(min_latency, track->get_real_latency());
// }
//
// for (size_t i = 1; i < tracks_.size(); i++) {
// mixer_track* track = tracks_[i];
// track->compensator_.update_buffer(min_latency, max_latency);
// }
// 计算所有轨道链的最大延迟和最小延迟
const auto& all_tracks = g_mixer.get_tracks();
const dummy_track* master = g_mixer.get_master();
for (mixer_track* track : all_tracks) {
if (track == master) continue;
int32_t latency = 0;
calc_latency(track, latency);
max_latency = std::max(max_latency, latency);
min_latency = std::min(min_latency, latency);
}
const int32_t latency_diff = max_latency - min_latency;
for (mixer_track_link* link : links_) {
if (link->to() != master) continue;
const int32_t track_latency = link->from()->get_latency();
const int32_t to_master_latency = latency_diff - track_latency;
link->compensator().set_latency(to_master_latency);
link->from()->set_ui_buffer_latency(to_master_latency);
}
}
void mixer_processor::add_link(mixer_track* from, mixer_track* to, float in_send_level) {
mixer_track_link* new_link = obj_mempool<mixer_track_link>::alloc(from, to, in_send_level);
auto* new_link = new mixer_track_link(from, to, in_send_level);
links_.push_back(new_link);
// 将连接的轨道加入到层次结构中
layer_tracks_[from].push_back(to);
track_map_[to].push_back(new_link);
update_all();
}
void mixer_processor::remove_link(mixer_track* from, mixer_track* to) {
const auto it = std::ranges::find_if(links_, [from, to](const auto& link) {
return link->from == from && link->send_to == to;
return link->from() == from && link->to() == to;
});
if (it != links_.end()) {
obj_mempool<mixer_track_link>::free(*it);
delete *it;
links_.erase(it);
// 更新层次结构
std::ranges::remove(layer_tracks_[from], to);
if (layer_tracks_[from].empty()) {
layer_tracks_.erase(from);
}
update_all();
}
}
void mixer_processor::remove_track(mixer_track *track) {
void mixer_processor::remove_track(mixer_track* track) {
const auto all_links = obj_mempool<mixer_track_link>::objs();
for (auto link : all_links) {
if (link->from() == track || link->to() == track) {
delete link;
}
}
track_map_.erase(track);
}
void mixer_processor::calc_latency(mixer_track* track, int32_t& latency) {
latency += track->get_latency();
const auto& track_child_links = track_map_[track];
for (const mixer_track_link* link : track_child_links) {
const mixer_track* target_track = link->to();
if (target_track == g_mixer.get_master())
continue;
mixer_track* child_track = link->from();
calc_latency(child_track, latency);
}
}

View File

@ -1,22 +1,31 @@
#pragma once
#include "latency_compensator.h"
#include "misc/mempool.h"
#include "taskflow/taskflow.hpp"
class mixer_track;
struct mixer_track_link {
mixer_track_link() = default;
mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level) : from(from_track), send_to(to), send_level(in_send_level) {
}
mixer_track* from = nullptr;
mixer_track* send_to = nullptr;
float send_level = 1.0f;
latency_compensator compensator;
struct mixer_track_link : public pool_obj<mixer_track_link> {
mixer_track_link();
mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level);
[[nodiscard]] auto from() const -> mixer_track* { return from_; }
[[nodiscard]] auto to() const -> mixer_track* { return send_to_; }
[[nodiscard]] auto send_level() const -> float { return send_level_; }
[[nodiscard]] auto compensator() -> latency_compensator& { return compensator_; }
void process();
private:
mixer_track* from_ = nullptr;
mixer_track* send_to_ = nullptr;
float send_level_ = 1.0f;
latency_compensator compensator_;
};
class mixer_processor {
public:
void process(uint32_t in_frames);
void pop_master(uint32_t in_frames, circular_buffer_vector_type& out_buffer);
void update_all() {
@ -26,14 +35,16 @@ public:
void update_taskflow();
void update_latency();
void add_link(mixer_track* from, mixer_track* to, float in_send_level);
void remove_link(mixer_track* from, mixer_track* to);
void remove_track(mixer_track* track);
private:
void calc_latency(mixer_track* track, int32_t& latency);
std::vector<tf::Taskflow> taskflow_;
std::vector<mixer_track_link*> links_;
std::unordered_map<mixer_track*, std::vector<mixer_track*>> layer_tracks_;
std::atomic<uint32_t> task_frame_ = 0;
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>> track_map_; // [to, links] 表示某个轨道会发送到哪些轨道
};

View File

@ -8,7 +8,7 @@
mixer_track::~mixer_track() {
delete channel_interface_;
channel_interface_ = nullptr;
for (auto e : effects) {
for (auto e : effects_) {
// e->on_latency_changed.remove_all(this);
delete_effect(this, e);
}
@ -19,10 +19,10 @@ void mixer_track::init() {
uint32_t block_size = g_audio_device_manager.get_buffer_size();
buffer_.resize(channel_count, block_size);
ui_buffers.resize(channel_count);
for (auto& buffer : ui_buffers) {
buffer.set_capacity(block_size * 2);
}
ui_buffers_.resize(channel_count);
for (auto& buffer : ui_buffers_) {
buffer.set_capacity(block_size * 2);
}
channel_interface_ = new mixer_channel_interface(this);
}
@ -32,7 +32,7 @@ void mixer_track::add_effect(plugin_host* in_effect) {
in_effect->owner_tracks.push_back(this);
in_effect->channel->set_input_channel(channel_interface_->output_channel_nodes);
in_effect->channel->set_output_channel(channel_interface_->output_channel_nodes);
effects.push_back(in_effect);
effects_.push_back(in_effect);
});
}
@ -41,44 +41,53 @@ void mixer_track::remove_effect(plugin_host* in_effect) {
auto remove_effect_track = std::remove(in_effect->owner_tracks.begin(), in_effect->owner_tracks.end(), this);
in_effect->owner_tracks.erase(remove_effect_track, in_effect->owner_tracks.end());
auto remove_effect = std::remove(effects.begin(), effects.end(), in_effect);
effects.erase(remove_effect, effects.end());
auto remove_effect = std::remove(effects_.begin(), effects_.end(), in_effect);
effects_.erase(remove_effect, effects_.end());
});
}
void mixer_track::process(uint32_t in_frames) {
for (auto effect : effects)
effect->process(in_frames);
for (auto effect : effects_) effect->process(in_frames);
buffer_.multiple(get_volume());
}
int32_t mixer_track::get_latency() const {
uint32_t latency = 0;
for (const auto effect : effects) {
auto mixer_track::get_latency() const -> int32_t {
int32_t latency = 0;
for (const auto effect : effects_) {
latency += effect->get_latency();
}
return latency;
}
instrument_track::instrument_track(plugin_host* in_instrument): mixer_track(mixer_track_type::instrument), instrument_(in_instrument) {
void mixer_track::set_ui_buffer_latency(int32_t in_latency) {
const uint32_t block_size = g_audio_device_manager.get_buffer_size();
const int32_t block_num = std::ceil((float)in_latency / block_size) + 1;
for (auto& buffer : ui_buffers_) {
buffer.set_capacity(block_num * block_size);
buffer.push_zeros(in_latency);
}
}
instrument_track::instrument_track(plugin_host* in_instrument): mixer_track(mixer_track_type::instrument)
, instrument_(in_instrument) {}
void instrument_track::rename(const std::string& in_name) {
instrument_->name = in_name;
}
std::string instrument_track::get_name() const {
auto instrument_track::get_name() const -> std::string {
return instrument_->name;
}
int32_t instrument_track::get_latency() const {
return mixer_track::get_latency() + instrument_->get_latency();
auto instrument_track::get_latency() const -> int32_t {
return mixer_track::get_latency() + instrument_->get_latency() + instrument_->get_user_latency();
}
void delete_effect(mixer_track* track, plugin_host* host) {
track->remove_effect(host);
host->try_close_editor();
g_audio_thread_hub.push_message([host, track](){
g_audio_thread_hub.push_message([host, track]() {
delete host;
});
}

View File

@ -1,7 +1,9 @@
#pragma once
#include <ranges>
#include <utility>
#include "latency_compensator.h"
#include "../../misc/mempool.h"
#include "audio/misc/audio_buffer.h"
#include "audio/misc/circular_audio_buffer.h"
#include "misc/delegates.h"
@ -18,70 +20,94 @@ enum class mixer_track_type {
class CORE_API mixer_track {
friend class mixer;
public:
explicit mixer_track(mixer_track_type in_type) : type_(in_type), id_(gen_uid()) {}
[[nodiscard]] uint64_t get_uid() const { return id_; }
explicit mixer_track(mixer_track_type in_type) : type_(in_type)
, id_(gen_uid()) {}
[[nodiscard]] auto get_uid() const -> uint64_t { return id_; }
virtual ~mixer_track();
void init();
void add_effect(plugin_host* in_effect);
void remove_effect(plugin_host* in_effect);
void process(uint32_t in_frames);
virtual void rename(const std::string& in_name) = 0;
[[nodiscard]] virtual std::string get_name() const = 0;
[[nodiscard]] sample_t** get_headers() { return buffer_.get_headers(); }
[[nodiscard]] const std::vector<sample_t*>& get_headers_vector() { return buffer_.get_headers_vector(); }
[[nodiscard]] virtual auto get_name() const -> std::string = 0;
[[nodiscard]] auto get_headers() -> sample_t** { return buffer_.get_headers(); }
[[nodiscard]] auto get_headers_vector() -> const std::vector<sample_t*>& { return buffer_.get_headers_vector(); }
[[nodiscard]] auto get_buffer() -> audio_buffer& { return buffer_; }
void clear() { buffer_.clear(); }
[[nodiscard]] mixer_track_type get_type() const { return type_; }
[[nodiscard]] auto get_type() const -> mixer_track_type { return type_; }
[[nodiscard]] channel_interface* get_channel_interface() const { return channel_interface_; }
[[nodiscard]] auto get_channel_interface() const -> channel_interface* { return channel_interface_; }
void set_volume(float in_volume) { volume_ = in_volume; }
[[nodiscard]] float get_volume() const { return volume_; }
[[nodiscard]] auto get_volume() const -> float { return volume_; }
[[nodiscard]] virtual int32_t get_latency() const;
[[nodiscard]] virtual auto get_latency() const -> int32_t;
[[nodiscard]] std::string get_imgui_id() const {
[[nodiscard]] auto get_imgui_id() const -> std::string {
return get_name() + "##" + std::to_string(id_);
}
std::vector<plugin_host*> effects{};
void set_ui_buffer_latency(int32_t in_latency);
circular_buffer_vector_type ui_buffers;
void push_ui_buffer(const audio_buffer& in_buffer) {
auto headers = in_buffer.get_headers_vector();
for (auto header : std::ranges::views::zip(headers, ui_buffers_)) {
const auto& [in, out] = header;
out.push(in, in_buffer.get_num_samples());
}
}
[[nodiscard]] auto get_ui_buffer() -> circular_buffer_vector_type& { return ui_buffers_;}
[[nodiscard]] auto get_effects() const -> const std::vector<plugin_host*>& { return effects_; }
private:
audio_buffer buffer_;
const mixer_track_type type_;
float volume_ = 1.0f;
circular_buffer_vector_type ui_buffers_;
std::vector<plugin_host*> effects_{};
audio_buffer buffer_;
channel_interface* channel_interface_ = nullptr;
const uint64_t id_ = 0;
float volume_ = 1.0f;
};
class instrument_track : public mixer_track {
class instrument_track : public mixer_track, public pool_obj<instrument_track> {
public:
static constexpr auto type = mixer_track_type::instrument;
explicit instrument_track(plugin_host* in_instrument);
void rename(const std::string& in_name) override;
[[nodiscard]] std::string get_name() const override;
[[nodiscard]] plugin_host* get_instrument() const { return instrument_; }
[[nodiscard]] auto get_name() const -> std::string override;
[[nodiscard]] auto get_instrument() const -> plugin_host* { return instrument_; }
[[nodiscard]] auto get_latency() const -> int32_t override;
[[nodiscard]] int32_t get_latency() const override;
private:
plugin_host* instrument_;
};
class dummy_track : public mixer_track {
class dummy_track : public mixer_track, public pool_obj<dummy_track> {
public:
static constexpr auto type = mixer_track_type::dummy;
dummy_track() : mixer_track(mixer_track_type::dummy) {}
void rename(const std::string& in_name) override { name_ = in_name; }
[[nodiscard]] std::string get_name() const override { return name_; }
[[nodiscard]] auto get_name() const -> std::string override { return name_; }
private:
std::string name_;
};

View File

@ -4,6 +4,7 @@
#include <vector>
#include "extern.h"
#include "host_param.h"
#include "../../misc/mempool.h"
#include "audio/misc/circular_audio_buffer.h"
#include "misc/delegates.h"
@ -18,56 +19,79 @@ struct host_param_info {
};
enum class plugin_host_type {
vst2,
vst3,
au,
aax,
lv2,
internal,
vst2,
vst3,
au,
aax,
lv2,
internal,
};
class CORE_API plugin_host {
friend class window_manager;
public:
plugin_host(plugin_host_type in_type) : id(gen_uid()), type_(in_type) {
}
plugin_host(plugin_host_type in_type) : id(gen_uid())
, type_(in_type) {}
virtual ~plugin_host();
[[nodiscard]] uint64_t get_uid() const { return id; }
[[nodiscard]] plugin_host_type get_type() const { return type_; }
[[nodiscard]] uint64_t get_uid() const { return id; }
[[nodiscard]] plugin_host_type get_type() const { return type_; }
virtual bool load_plugin(const char* path) = 0;
virtual void set_enabled(bool enabled) = 0;
[[nodiscard]] virtual bool is_enabled() const = 0;
virtual void on_update_sample_rate(double sample_rate) = 0;
virtual void on_update_buffer_size(int buffer_size);
[[nodiscard]] virtual uint32_t get_input_channels() const = 0;
[[nodiscard]] virtual uint32_t get_output_channels() const = 0;
virtual void update_channel_node_name() {}
virtual void process(uint32_t frame_num) = 0;
void try_open_editor();
void try_close_editor();
virtual void toggle_editor();
virtual void idle_editor() {}
void set_user_latency(int32_t latency) { user_latency_ = latency; }
[[nodiscard]] auto get_user_latency() const -> int32_t { return user_latency_; }
[[nodiscard]] virtual bool has_editor() const { return false; }
virtual void get_editor_size(uint32_t& width, uint32_t& height) const = 0;
[[nodiscard]] virtual int32_t get_latency() const { return 0.0f; }
[[nodiscard]] virtual std::string load_name() const { return ""; }
[[nodiscard]] virtual std::string load_vendor() const { return ""; }
[[nodiscard]] virtual std::string get_parameter_name(int index) const = 0;
[[nodiscard]] virtual std::string get_parameter_label(int index) const { return ""; };
[[nodiscard]] virtual std::string get_parameter_display(int index) const { return ""; };
[[nodiscard]] virtual int32_t get_param_count() const = 0;
[[nodiscard]] virtual bool is_param_index_valid(const int32_t index) const { return index >= 0 && index < get_param_count(); }
[[nodiscard]] virtual bool is_param_index_valid(const int32_t index) const {
return index >= 0 && index < get_param_count();
}
virtual void set_param_value(int32_t index, float value) = 0;
virtual float get_param_value(int32_t index) = 0;
virtual float get_param_min_value(int32_t index) { return 0.0f; }
virtual float get_param_max_value(int32_t index) { return 1.0f; }
@ -81,16 +105,21 @@ public:
[[nodiscard]] std::string get_imgui_id() const {
return name + "##" + std::to_string(id);
}
std::shared_ptr<circular_buffer_vector_type> ui_buffers;
multicast_delegate<int32_t, float> on_param_changed; // int32_t param_index, float value
multicast_delegate<int32_t> on_latency_changed; // int32_t latency_sample
protected:
void create_and_open_editor();
void destroy_editor();
virtual void on_open_editor(void* window_handle) = 0;
virtual void on_close_editor() = 0;
private:
bool editor_opened = false;
const uint64_t id = 0;
plugin_host_type type_;
plugin_host_type type_;
int32_t user_latency_ = 0;
};

View File

@ -24,14 +24,14 @@ void plugin_host_manager::release(singleton_release_guard& release_guard) {
singleton_t<plugin_host_manager>::release(release_guard);
release_guard.require_release<audio_device_manager>();
for (const plugin_host* host: plugin_hosts_) {
free_plugin_host(host);
for (plugin_host* host: plugin_hosts_) {
delete host;
}
plugin_hosts_.clear();
instrument_plugins_.clear();
}
plugin_host *plugin_host_manager::create_effect_plugin_host(const char *path) {
plugin_host* plugin_host_manager::create_effect_plugin_host(const char* path) {
auto host = load_plugin(path);
return host;
}
@ -61,7 +61,7 @@ void plugin_host_manager::remove_instrument_plugin_host(plugin_host* host) {
}
plugin_host* plugin_host_manager::load_plugin(const char* path) {
auto host = alloc_plugin_host<vst2_plugin_host>();
auto host = new vst2_plugin_host();
try {
host->load_plugin(path);
} catch (std::exception& e) {

View File

@ -3,49 +3,48 @@
#include "misc/singleton/singleton.h"
#include "taskflow/taskflow.hpp"
#include "plugin_host.h"
#include "mempool.h"
#include "../../misc/mempool.h"
#include <unordered_map>
class mixer_track;
class CORE_API plugin_host_manager : public singleton_t<plugin_host_manager> {
public:
void init(singleton_initliazer& initliazer) override;
void begin_release(singleton_release_guard& release_guard) override;
void release(singleton_release_guard& release_guard) override;
void init(singleton_initliazer& initliazer) override;
plugin_host* create_effect_plugin_host(const char* path);
plugin_host* create_instrument_plugin_host(const char* path);
void remove_instrument_plugin_host(plugin_host* host);
void begin_release(singleton_release_guard& release_guard) override;
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
const std::vector<plugin_host*>& get_instrument_hosts() { return instrument_plugins_; }
void process(uint32_t in_frames);
void release(singleton_release_guard& release_guard) override;
const char* get_name() override { return "plugin_host_manager"; }
plugin_host* create_effect_plugin_host(const char* path);
plugin_host* create_instrument_plugin_host(const char* path);
void remove_instrument_plugin_host(plugin_host* host);
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
const std::vector<plugin_host*>& get_instrument_hosts() { return instrument_plugins_; }
void process(uint32_t in_frames);
const char* get_name() override { return "plugin_host_manager"; }
multicast_delegate<plugin_host*> on_instrument_added;
multicast_delegate<plugin_host*> on_instrument_removed;
multicast_delegate<plugin_host*> on_instrument_added;
multicast_delegate<plugin_host*> on_instrument_removed;
private:
plugin_host* load_plugin(const char* path);
void register_instrument_plugin(plugin_host* host);
void on_mixer_track_removed(mixer_track* track);
plugin_host* load_plugin(const char* path);
template <typename T>
T* alloc_plugin_host() {
auto* host = plugin_host_map_[T::type].template alloc<T>();
return host;
}
template <typename T>
void free_plugin_host(T* host) {
plugin_host_map_[host->get_type()].free(host);
}
std::vector<plugin_host*> instrument_plugins_{};
std::vector<plugin_host*> plugin_hosts_{};
std::unordered_map<plugin_host_type, mempool<>> plugin_host_map_;
void register_instrument_plugin(plugin_host* host);
void update_taskflow(uint32_t in_frames);
tf::Taskflow taskflow_;
void on_mixer_track_removed(mixer_track* track);
std::vector<plugin_host*> instrument_plugins_{};
std::vector<plugin_host*> plugin_hosts_{};
void update_taskflow(uint32_t in_frames);
tf::Taskflow taskflow_;
};
DEFINE_SINGLETON_INSTANCE(plugin_host_manager)

View File

@ -2,31 +2,19 @@
#include "audio/device/audio_device_manager.h"
#include "audio/mixer/channel_interface.h"
#include "audio/mixer/latency_compensator.h"
#include "window/window_manager.h"
std::unordered_map<std::string, std::weak_ptr<dynamic_library>> vst2_library_map;
std::unordered_map<void*, uint32_t> vst2_latency_map;
VstTimeInfo vst2_plugin_host::vst_time_info{};
std::unordered_map<std::string, bool> can_do_map =
{
{"sendVstEvents", true},
{"sendVstMidiEvent", true},
{"sendVstTimeInfo", true},
{"receiveVstEvents", false},
{"receiveVstMidiEvent", false},
{"receiveVstTimeInfo", false},
{"offline", false},
{"reportConnectionChanges", false},
{"acceptIOChanges", true},
{"sizeWindow", true},
{"asyncProcessing", true},
{"supplyIdle", true},
{"supportShell", false},
{"openFileSelector", false},
{"editFile", false},
{"closeFileSelector", false},
{"NIMKPIVendorSpecificCallbacks", false},
std::unordered_map<std::string, bool> can_do_map = {
{"sendVstEvents", true}, {"sendVstMidiEvent", true}, {"sendVstTimeInfo", true}, {"receiveVstEvents", false},
{"receiveVstMidiEvent", false}, {"receiveVstTimeInfo", false}, {"offline", false},
{"reportConnectionChanges", false}, {"acceptIOChanges", true}, {"sizeWindow", true}, {"asyncProcessing", true},
{"supplyIdle", true}, {"supportShell", false}, {"openFileSelector", false}, {"editFile", false},
{"closeFileSelector", false}, {"NIMKPIVendorSpecificCallbacks", false},
};
std::shared_ptr<dynamic_library> get_vst2_library(const std::string& path) {
@ -38,109 +26,98 @@ std::shared_ptr<dynamic_library> get_vst2_library(const std::string& path) {
return library;
}
bool vst_host_can_do(const char* can_do)
{
if (can_do_map.contains(can_do))
return can_do_map[can_do];
bool vst_host_can_do(const char* can_do) {
if (can_do_map.contains(can_do)) return can_do_map[can_do];
spdlog::warn("Unknown can do: {}", can_do);
return false;
}
VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
switch (opcode)
{
case audioMasterAutomate: {
auto host = ((vst2_plugin_host *) effect->user);
VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) {
switch (opcode) {
case audioMasterAutomate : {
auto host = ((vst2_plugin_host*) effect->user);
host->on_param_changed.broadcast(index, opt);
#if BUILD_DEBUG
#if BUILD_DEBUG
spdlog::info("vst2 plugin {} automate, {} index = {}, value = {}", host->name, host->get_parameter_name(index), index, opt);
#endif
#endif
auto old_latency = vst2_latency_map[host];
auto new_latency = host->get_latency();
if (old_latency != new_latency) {
vst2_latency_map[host] = new_latency;
host->on_latency_changed.broadcast(new_latency);
#if BUILD_DEBUG
on_latency_offset_changed.broadcast();
#if BUILD_DEBUG
spdlog::info("vst2 plugin {} latency changed: {}", host->name, new_latency);
#endif
#endif
}
return 1;
}
case audioMasterVersion:
case audioMasterVersion :
return kVstVersion; // 返回插件版本号
case audioMasterWantMidi:
case audioMasterWantMidi :
return 1;
case audioMasterGetSampleRate:
case audioMasterGetSampleRate :
return g_audio_device_manager.get_sample_rate(); // 返回采样率
case audioMasterGetCurrentProcessLevel:
case audioMasterGetCurrentProcessLevel :
return kVstProcessLevelRealtime; // 返回当前处理级别
case audioMasterGetBlockSize:
case audioMasterGetBlockSize :
return g_audio_device_manager.get_buffer_size(); // 返回块大小
case audioMasterSizeWindow:
{
case audioMasterSizeWindow : {
// 设置插件窗口大小
g_window_manager.resize_plugin_window((plugin_host*)effect->user, index, value);
g_window_manager.resize_plugin_window((plugin_host*) effect->user, index, value);
return 1;
}
case audioMasterGetTime:
return (VstIntPtr)&vst2_time;
case audioMasterIdle:
case audioMasterGetTime :
return (VstIntPtr) &vst2_time;
case audioMasterIdle :
// UE_LOG(LogTemp, Log, TEXT("Plugin Idle"));
return 1;
case audioMasterNeedIdle:
return 1;
case audioMasterNeedIdle :
// UE_LOG(LogTemp, Log, TEXT("Plugin Need Idle"));
return 1;
case audioMasterGetVendorString:
{
const char* Ptr1 = (const char*)ptr;
return 1;
case audioMasterGetVendorString : {
const char* Ptr1 = (const char*) ptr;
Ptr1 = "Arona";
return 1;
}
case audioMasterGetProductString:
{
const char* Ptr1 = (const char*)ptr;
case audioMasterGetProductString : {
const char* Ptr1 = (const char*) ptr;
Ptr1 = "Arona";
return 1;
}
case audioMasterGetVendorVersion:
case audioMasterGetVendorVersion :
return 1000;
case audioMasterCanDo:
{
const char* Ptr1 = (const char*)ptr;
case audioMasterCanDo : {
const char* Ptr1 = (const char*) ptr;
return vst_host_can_do(Ptr1) ? 1 : 0;
}
case audioMasterUpdateDisplay:
case audioMasterUpdateDisplay :
return 1;
case audioMasterBeginEdit:
{
case audioMasterBeginEdit : {
// UE_LOG(LogTemp, Log, TEXT("Want Begin Edit"));
return 1;
}
case audioMasterEndEdit:
{
case audioMasterEndEdit : {
// UE_LOG(LogTemp, Log, TEXT("Want End Edit"));
return 1;
}
case audioMasterVendorSpecific:
case audioMasterVendorSpecific :
return 0;
case audioMasterProcessEvents:
case audioMasterProcessEvents :
// TODO
{
// VstEvents* Events = (VstEvents*)ptr;
// Events->events[0]->type;
// FVST2PluginHost* Host = static_cast<FVST2PluginHost*>(Effect->user);
}
case audioMasterGetInputLatency:
case audioMasterGetInputLatency :
spdlog::info("Get Input Latency: {}", value);
return 0;
case audioMasterGetOutputLatency:
case audioMasterGetOutputLatency :
spdlog::info("Get Output Latency: {}", value);
return 0;
return 1;
default: break;
// ensureMsgf(0, TEXT("No Implement OpCode: %d"), OpCode);
return 1;
default : break;
// ensureMsgf(0, TEXT("No Implement OpCode: %d"), OpCode);
}
return 1;
@ -165,7 +142,7 @@ bool vst2_plugin_host::load_plugin(const char* path) {
// load vst
library_ = get_vst2_library(path);
// get main function
const auto entry_proc = (vst_plugin_entry_proc)(library_->get_function("VSTPluginMain"));
const auto entry_proc = (vst_plugin_entry_proc) (library_->get_function("VSTPluginMain"));
if (!entry_proc) {
spdlog::error("Failed to get entry point from {}", path);
return false;
@ -175,8 +152,7 @@ bool vst2_plugin_host::load_plugin(const char* path) {
if (!effect_) {
return false;
}
if (effect_->magic != kEffectMagic)
throw std::runtime_error("Not a VST2 plugin");
if (effect_->magic != kEffectMagic) throw std::runtime_error("Not a VST2 plugin");
effect_->user = this;
name = load_name();
@ -213,20 +189,16 @@ uint32_t vst2_plugin_host::get_output_channels() const {
}
void vst2_plugin_host::update_channel_node_name() {
for (int i = 0; i < effect_->numInputs; ++i)
{
for (int i = 0; i < effect_->numInputs; ++i) {
VstPinProperties pin_properties{};
if (dispatch(effGetInputProperties, i, 0, &pin_properties) == 1)
{
if (dispatch(effGetInputProperties, i, 0, &pin_properties) == 1) {
channel->set_input_channel_node_name(i / 2, i, pin_properties.label);
}
}
for (int i = 0; i < effect_->numOutputs; ++i)
{
for (int i = 0; i < effect_->numOutputs; ++i) {
VstPinProperties pin_properties{};
if (dispatch(effGetOutputProperties, i, 0, &pin_properties) == 1)
{
if (dispatch(effGetOutputProperties, i, 0, &pin_properties) == 1) {
channel->set_output_channel_node_name(i / 2, i, pin_properties.label);
}
}
@ -234,14 +206,14 @@ void vst2_plugin_host::update_channel_node_name() {
void vst2_plugin_host::process(uint32_t frame_num) {
// TODO send midi
#if USE_DOUBLE_SAMPLE
#if USE_DOUBLE_SAMPLE
effect_->processDoubleReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), static_cast<VstInt32>(frame_num));
#else
effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), static_cast<VstInt32>(frame_num));
#endif
#else
effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(),
static_cast<VstInt32>(frame_num));
#endif
for (int i = 0; i < ui_buffers->size(); ++i)
{
for (int i = 0; i < ui_buffers->size(); ++i) {
ui_buffers->at(i).push(channel->get_output_headers()[i], frame_num);
}
}
@ -283,14 +255,12 @@ std::string vst2_plugin_host::load_vendor() const {
}
void vst2_plugin_host::on_open_editor(void* window_handle) {
if (!has_editor())
return;
if (!has_editor()) return;
dispatch(effEditOpen, 0, 0, window_handle);
}
void vst2_plugin_host::on_close_editor() {
if (!has_editor())
return;
if (!has_editor()) return;
dispatch(effEditClose);
}
@ -316,46 +286,34 @@ std::string vst2_plugin_host::get_parameter_display(int index) const {
return buffer;
}
int32_t vst2_plugin_host::get_param_count() const
{
if (!effect_)
return 0;
int32_t vst2_plugin_host::get_param_count() const {
if (!effect_) return 0;
return effect_->numParams;
}
void vst2_plugin_host::set_param_value(int32_t index, float value)
{
if (effect_)
effect_->setParameter(effect_, index, value);
void vst2_plugin_host::set_param_value(int32_t index, float value) {
if (effect_) effect_->setParameter(effect_, index, value);
}
float vst2_plugin_host::get_param_value(int32_t index)
{
if (!effect_)
return 0;
float vst2_plugin_host::get_param_value(int32_t index) {
if (!effect_) return 0;
return effect_->getParameter(effect_, index);
}
float vst2_plugin_host::get_param_min_value(int32_t index) {
VstParameterProperties param_properties{};
if (dispatch(effGetParameterProperties, index, 0, &param_properties) == 1)
{
if (param_properties.flags & kVstParameterUsesIntegerMinMax)
return param_properties.minInteger;
if (param_properties.flags & kVstParameterUsesFloatStep)
return param_properties.smallStepFloat;
if (dispatch(effGetParameterProperties, index, 0, &param_properties) == 1) {
if (param_properties.flags & kVstParameterUsesIntegerMinMax) return param_properties.minInteger;
if (param_properties.flags & kVstParameterUsesFloatStep) return param_properties.smallStepFloat;
}
return plugin_host::get_param_min_value(index);
}
float vst2_plugin_host::get_param_max_value(int32_t index) {
VstParameterProperties param_properties{};
if (dispatch(effGetParameterProperties, index, 0, &param_properties) == 1)
{
if (param_properties.flags & kVstParameterUsesIntegerMinMax)
return param_properties.maxInteger;
if (param_properties.flags & kVstParameterUsesFloatStep)
return param_properties.largeStepFloat;
if (dispatch(effGetParameterProperties, index, 0, &param_properties) == 1) {
if (param_properties.flags & kVstParameterUsesIntegerMinMax) return param_properties.maxInteger;
if (param_properties.flags & kVstParameterUsesFloatStep) return param_properties.largeStepFloat;
}
return plugin_host::get_param_max_value(index);
}

View File

@ -4,7 +4,7 @@
#include "pluginterfaces/vst2.x/aeffect.h"
#include "pluginterfaces/vst2.x/aeffectx.h"
class vst2_plugin_host : public plugin_host {
class vst2_plugin_host : public plugin_host, public pool_obj<vst2_plugin_host> {
public:
static VstTimeInfo vst_time_info;
static constexpr auto type = plugin_host_type::vst2;

121
core/misc/mempool.h Normal file
View File

@ -0,0 +1,121 @@
#pragma once
#include <memory>
#include <vector>
class memory_pool {
public:
memory_pool(const size_t chunk_size, const size_t chunks_per_block)
: chunk_size_(chunk_size), chunks_per_block_(chunks_per_block), current_block_(nullptr), free_list_(nullptr) {
allocate_block();
}
memory_pool(memory_pool&& other) noexcept
: chunk_size_(other.chunk_size_), chunks_per_block_(other.chunks_per_block_), blocks_(std::move(other.blocks_)),
current_block_(other.current_block_), free_list_(other.free_list_) {
other.current_block_ = nullptr;
other.free_list_ = nullptr;
}
~memory_pool() {
for (void* block : blocks_) {
delete[] static_cast<char*>(block);
}
}
auto allocate() -> void* {
if (!free_list_) {
allocate_block();
}
std::lock_guard lock(mutex_);
void* chunk = free_list_;
free_list_ = *static_cast<void**>(free_list_);
return chunk;
}
void deallocate(void* chunk) {
std::lock_guard lock(mutex_);
*static_cast<void**>(chunk) = free_list_;
free_list_ = chunk;
}
private:
void allocate_block() {
const size_t block_size = chunk_size_ * chunks_per_block_;
const auto new_block = new char[block_size];
{
std::lock_guard lock(mutex_);
blocks_.push_back(new_block);
current_block_ = new_block;
}
for (size_t i = 0; i < chunks_per_block_; ++i) {
void* chunk = new_block + i * chunk_size_;
deallocate(chunk);
}
}
size_t chunk_size_;
size_t chunks_per_block_;
std::vector<void*> blocks_;
void* current_block_;
void* free_list_;
std::mutex mutex_;
};
template<class T>
class obj_mempool {
public:
template<typename ...Args>
static auto alloc(Args&&... args) -> T* {
auto obj = static_cast<T*>(pool_.allocate());
new (obj) T(std::forward<Args>(args)...);
objs_.push_back(obj);
return obj;
}
static auto alloc() -> T* {
auto obj = static_cast<T*>(pool_.allocate());
new (obj) T();
objs_.push_back(obj);
return obj;
}
static void free(T* p) {
p->~T();
pool_.deallocate(p);
objs_.erase(std::remove(objs_.begin(), objs_.end(), p), objs_.end());
}
static void free_all() {
for (auto obj : objs_) {
obj->~T();
pool_.deallocate(obj);
}
objs_.clear();
}
static auto objs() -> const std::vector<T*>& {
return objs_;
}
static auto has_obj(T* p) -> bool {
return std::find(objs_.begin(), objs_.end(), p) != objs_.end();
}
static auto safe_free(T* p) -> bool {
if (has_obj(p)) {
free(p);
return true;
}
return false;
}
private:
inline static memory_pool pool_ = memory_pool(sizeof(T), 64);
inline static std::vector<T*> objs_;
};
template<class T>
class pool_obj {
inline static memory_pool pool = memory_pool(sizeof(T), 64);
public:
auto operator new(size_t size) -> void* {
return pool.allocate();
}
void operator delete(void* p) {
pool.deallocate(p);
}
};

View File

@ -4,9 +4,9 @@
void thread_message_hub::process_messages() {
while (!messages_.empty()) {
thread_message* message = messages_.front();
lamba_thread_message* message = messages_.front();
message->execute();
mem_pool_.free(message);
obj_mempool<lamba_thread_message>::free(message);
messages_.pop();
}
}

View File

@ -2,30 +2,29 @@
#include <queue>
#include "mempool.h"
#include "../misc/mempool.h"
#include "thread_message.h"
class CORE_API thread_message_hub {
public:
template<typename T, typename ...Args>
void push_message(Args&&... args) {
T* message = mem_pool_.alloc<T>(std::forward<Args>(args)...);
push_message(message);
}
template<typename T>
void push_message() {
T* message = mem_pool_.alloc<T>();
push_message(message);
}
// template<typename T, typename ...Args>
// void push_message(Args&&... args) {
// T* message = obj_mempool<T>::alloc(std::forward<Args>(args)...);
// push_message(message);
// }
// template<typename T>
// void push_message() {
// T* message = obj_mempool<T>::alloc();
// push_message(message);
// }
void push_message(const std::function<void()>& func) {
auto* message = mem_pool_.alloc<lamba_thread_message>(func);
auto* message = obj_mempool<lamba_thread_message>::alloc(func);
push_message(message);
}
void process_messages();
private:
void push_message(thread_message* message) {
void push_message(lamba_thread_message* message) {
messages_.push(message);
}
std::queue<thread_message*> messages_;
mempool<> mem_pool_ = mempool(1024000);
std::queue<lamba_thread_message*> messages_;
};

View File

@ -5,12 +5,7 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_CPP_STANDARD 17)
add_library(${PROJECT_NAME} SHARED
mempool/ncx_core.h
mempool/ncx_lock.h
mempool/ncx_slab.c
mempool/ncx_slab.h
mempool/my_unistd.h
mempool.h
../../core/misc/mempool.h
extern.h
)
include_directories(mempool)

View File

@ -1,96 +0,0 @@
#pragma once
#include "ncx_slab.h"
#include <cstdlib>
template<int32_t alignment = 64>
class mempool {
public:
mempool(size_t pool_size = 1024000) { // 1024KB
const auto space = static_cast<u_char*>(malloc(pool_size));
mem_pool_ = reinterpret_cast<ncx_slab_pool_t*>(space);
mem_pool_->addr = space;
mem_pool_->min_shift = 3;
mem_pool_->end = space + pool_size;
ncx_slab_init(mem_pool_);
}
~mempool() {
free(mem_pool_->addr);
}
[[nodiscard]] void* alloc(size_t size) const {
return ncx_slab_alloc(mem_pool_, size);
}
template<typename T, typename ...Args>
[[nodiscard]] T* alloc(Args&&... args) const {
T* out = (T*)alloc(sizeof(T));
new (out) T(std::forward<Args>(args)...);
return out;
}
template<typename T>
[[nodiscard]] T* alloc() const {
T* out = (T*)alloc(sizeof(T));
new (out) T();
return out;
}
template<typename T>
void free(T* p) const {
p->~T();
free((void*)p);
}
void free(void* p) const {
ncx_slab_free(mem_pool_, p);
}
[[nodiscard]] ncx_slab_stat_t stat() const {
ncx_slab_stat_t out;
ncx_slab_stat(mem_pool_, &out);
return out;
}
private:
ncx_slab_pool_t* mem_pool_ = nullptr;
};
template<class T>
class obj_mempool {
public:
template<typename ...Args>
static T* alloc(Args&&... args) {
auto obj = pool_.template alloc<T>(std::forward<Args>(args)...);
objs_.push_back(obj);
return obj;
}
static T* alloc() {
auto obj = pool_.template alloc<T>();
objs_.push_back(obj);
return obj;
}
static void free(T* p) {
pool_.template free<T>(p);
objs_.erase(std::remove(objs_.begin(), objs_.end(), p), objs_.end());
}
static void free_all() {
for (auto obj : objs_) {
pool_.template free<T>(obj);
}
objs_.clear();
}
static const std::vector<T*>& objs() {
return objs_;
}
static bool has_obj(T* p) {
return std::find(objs_.begin(), objs_.end(), p) != objs_.end();
}
static bool safe_free(T* p) {
if (has_obj(p)) {
free(p);
return true;
}
return false;
}
private:
static mempool<alignof(T)> pool_;
static std::vector<T*> objs_;
};

View File

@ -1,41 +0,0 @@
CC=gcc
CXX=g++
INC+=
LIB+=
#判断系统架构(32bit, 64bit)
ARCH := $(shell getconf LONG_BIT)
ifeq ($(ARCH),64)
CFLAGS+= -DNCX_PTR_SIZE=8
else
CFLAGS+= -DNCX_PTR_SIZE=4
endif
#CFLAGS+= -pipe -O0 -Wall -g3 -ggdb3
CFLAGS+= -pipe -O3
#定义是否打印日志
CFLAGS+= -DLOG_LEVEL=4
#是否与malloc类似模拟脏数据
#CFLAGS+= -DNCX_DEBUG_MALLOC
#是否自动合并碎片
CFLAGS+= -DPAGE_MERGE
TARGET=pool_test
ALL:$(TARGET)
OBJ= ncx_slab.o
$(TARGET):$(OBJ) main.o
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
pool_bench:$(OBJ) bench.o
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
%.o: %.c
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
clean:
rm -f *.o
rm -f $(TARGET) pool_bench
install:

View File

@ -1,106 +0,0 @@
ncx_mempool
======================
Description
===========
a nginx-based memory pool . <br/>
both share-memory and private-memory are supported. it is more efficient than malloc.<br/>
it is a very light weight and do not rely on any third-party library.
Examples
========
1 #include "ncx_slab.h"
2
3 int main(int argc, char **argv)
4 {
5 char *p;
6 size_t pool_size = 4096000; //4M
7 ncx_slab_stat_t stat;
8 u_char *space;
9 space = (u_char *)malloc(pool_size);
10
11 ncx_slab_pool_t *sp;
12 sp = (ncx_slab_pool_t*) space;
13
14 sp->addr = space;
15 sp->min_shift = 3;
16 sp->end = space + pool_size;
17
18 /*
19 * init
20 */
21 ncx_slab_init(sp);
22
23 /*
24 * alloc
25 */
26 p = ncx_slab_alloc(sp, 128);
27
28 /*
29 * memory usage api.
30 */
31 ncx_slab_stat(sp, &stat);
32
33 /*
34 * free
35 */
36 ncx_slab_free(sp, p);
37
38 free(space);
39
40 return 0;
41}
API
===
**ncx_slab_init(ncx_slab_pool_t *pool)** <br/>
**Description**: 初始化内存池结构;
**ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size)**<br/>
**Description**: 内存分配
**ncx_slab_free(ncx_slab_pool_t *pool, void *p)** <br/>
**Description**: 释放内存
**ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat)**<br/>
**Description**: 查看内存池使用情况
Customization
=============
正如example所示内存池内存是由应用层先分配ncx_mempool是在给定的内存基础上进行分配和回收管理。 <br/>
所以内存池本身不关心所管理的内存是私有内存 or 共享内存;
ncx_lock.h 是锁接口;根据实际需要重定义: <br/>
1.多线程共享内存池可参考pthread_spin_lock <br/>
2.多进程共享内存池可参考nginx的ngx_shmtx.c实现spin lock <br/>
3.单进程单线程使用内存池,无锁编程..
ncx_log.h 是日志接口,根据实际需要重定义.
ncx_slab.c.orz 是 ncx_slab.c的详细注释方便理解.
Author
======
dcshi(施俊伟) <dcshi@qq.com>
Copyright and License
=====================
This module is licensed under the BSD license.
Copyright (C) 2013, by dcshi(施俊伟). <dcshi@qq.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,79 +0,0 @@
#include "ncx_slab.h"
#include <sys/time.h>
uint64_t usTime()
{
struct timeval tv;
uint64_t usec;
gettimeofday(&tv, NULL);
usec = ((uint64_t)tv.tv_sec)*1000000LL;
usec += tv.tv_usec;
return usec;
}
int main(int argc, char **argv)
{
ncx_slab_pool_t *sp;
size_t pool_size;
u_char *space;
pool_size = 4096000; //4M
space = (u_char *)malloc(pool_size);
sp = (ncx_slab_pool_t*) space;
sp->addr = space;
sp->min_shift = 3;
sp->end = space + pool_size;
ncx_slab_init(sp);
char *p;
uint64_t us_begin;
uint64_t us_end;
size_t size[] = { 30, 120, 256, 500, 1000, 2000, 3000, 5000};
printf("size\tncx\tmalloc\tpct\n");
int i, j;
uint64_t t1, t2;
for (j = 0; j < sizeof(size)/sizeof(size_t); j++)
{
size_t s = size[j];
printf("%d\t", s);
//test for ncx_pool
us_begin = usTime();
for(i = 0; i < 1000000; i++)
{
p = ncx_slab_alloc(sp, s);
ncx_slab_free(sp, p);
}
us_end = usTime();
t1 = (us_end - us_begin);
printf("%llu \t", t1/1000);
// test for malloc
us_begin = usTime();
for(i = 0; i < 1000000; i++)
{
p = (char*)malloc(s);
free(p);
}
us_end = usTime();
t2 = (us_end - us_begin);
printf("%llu\t", t2/1000);
printf("%.2f\n", (double)t1 / (double)t2);
}
free(space);
return 0;
}

View File

@ -1,53 +0,0 @@
#include "ncx_slab.h"
int main(int argc, char **argv)
{
char *p;
size_t pool_size = 4096000; //4M
ncx_slab_stat_t stat;
u_char *space;
space = (u_char *)malloc(pool_size);
ncx_slab_pool_t *sp;
sp = (ncx_slab_pool_t*) space;
sp->addr = space;
sp->min_shift = 3;
sp->end = space + pool_size;
ncx_slab_init(sp);
int i;
for (i = 0; i < 1000000; i++)
{
p = ncx_slab_alloc(sp, 128 + i);
if (p == NULL)
{
printf("%d\n", i);
return -1;
}
ncx_slab_free(sp, p);
}
ncx_slab_stat(sp, &stat);
printf("##########################################################################\n");
for (i = 0; i < 2500; i++)
{
p = ncx_slab_alloc(sp, 30 + i);
if (p == NULL)
{
printf("%d\n", i);
return -1;
}
if (i % 3 == 0)
{
ncx_slab_free(sp, p);
}
}
ncx_slab_stat(sp, &stat);
free(space);
return 0;
}

View File

@ -1,12 +0,0 @@
#pragma once
#ifdef WIN32
#include <windows.h>
inline uintptr_t getpagesize() {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
return systemInfo.dwPageSize;
}
#else
#include <unistd.h>
#endif

View File

@ -1,25 +0,0 @@
#ifndef _NCX_CORE_H_
#define _NCX_CORE_H_
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdbool.h>
typedef unsigned char u_char;
typedef uintptr_t ncx_uint_t;
typedef intptr_t ncx_int_t;
#ifndef NCX_ALIGNMENT
#define NCX_ALIGNMENT sizeof(unsigned long) /* platform word */
#endif
#define ncx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
#define ncx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
#define ncx_memzero(buf, n) (void) memset(buf, 0, n)
#define ncx_memset(buf, c, n) (void) memset(buf, c, n)
#endif

View File

@ -1,13 +0,0 @@
#ifndef _NCX_LOCK_H_
#define _NCX_LOCK_H_
#define ncx_shmtx_lock(x) { /*void*/ }
#define ncx_shmtx_unlock(x) { /*void*/ }
typedef struct {
ncx_uint_t spin;
} ncx_shmtx_t;
#endif

View File

@ -1,848 +0,0 @@
#include "ncx_slab.h"
#include <my_unistd.h>
#define NCX_SLAB_PAGE_MASK 3
#define NCX_SLAB_PAGE 0
#define NCX_SLAB_BIG 1
#define NCX_SLAB_EXACT 2
#define NCX_SLAB_SMALL 3
#if (NCX_PTR_SIZE == 4)
#define NCX_SLAB_PAGE_FREE 0
#define NCX_SLAB_PAGE_BUSY 0xffffffff
#define NCX_SLAB_PAGE_START 0x80000000
#define NCX_SLAB_SHIFT_MASK 0x0000000f
#define NCX_SLAB_MAP_MASK 0xffff0000
#define NCX_SLAB_MAP_SHIFT 16
#define NCX_SLAB_BUSY 0xffffffff
#else /* (NCX_PTR_SIZE == 8) */
#define NCX_SLAB_PAGE_FREE 0
#define NCX_SLAB_PAGE_BUSY 0xffffffffffffffff
#define NCX_SLAB_PAGE_START 0x8000000000000000
#define NCX_SLAB_SHIFT_MASK 0x000000000000000f
#define NCX_SLAB_MAP_MASK 0xffffffff00000000
#define NCX_SLAB_MAP_SHIFT 32
#define NCX_SLAB_BUSY 0xffffffffffffffff
#endif
#if (NCX_DEBUG_MALLOC)
#define ncx_slab_junk(p, size) ncx_memset(p, 0xA5, size)
#else
#define ncx_slab_junk(p, size)
#endif
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool,
ncx_uint_t pages);
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
ncx_uint_t pages);
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page);
static ncx_uint_t ncx_slab_max_size;
static ncx_uint_t ncx_slab_exact_size;
static ncx_uint_t ncx_slab_exact_shift;
static ncx_uint_t ncx_pagesize;
static ncx_uint_t ncx_pagesize_shift;
static ncx_uint_t ncx_real_pages;
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_host.h>
#endif
void ncx_slab_init(ncx_slab_pool_t* pool) {
u_char* p;
size_t size;
ncx_uint_t i, n, pages;
ncx_slab_page_t* slots;
/*pagesize*/
#ifdef __APPLE__
vm_size_t pagesize;
host_page_size(mach_host_self(), &pagesize);
ncx_pagesize = pagesize;
#else
ncx_pagesize = getpagesize();
#endif
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
n >>= 1; ncx_pagesize_shift++) {
/* void */
}
/* STUB */
if (ncx_slab_max_size == 0) {
ncx_slab_max_size = ncx_pagesize / 2;
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
/* void */
}
}
pool->min_size = 1 << pool->min_shift;
p = (u_char*) pool + sizeof(ncx_slab_pool_t);
slots = (ncx_slab_page_t*) p;
n = ncx_pagesize_shift - pool->min_shift;
for (i = 0; i < n; i++) {
slots[i].slab = 0;
slots[i].next = &slots[i];
slots[i].prev = 0;
}
p += n * sizeof(ncx_slab_page_t);
size = pool->end - p;
ncx_slab_junk(p, size);
pages = (ncx_uint_t) (size / (ncx_pagesize + sizeof(ncx_slab_page_t)));
ncx_memzero(p, pages * sizeof(ncx_slab_page_t));
pool->pages = (ncx_slab_page_t*) p;
pool->free.prev = 0;
pool->free.next = (ncx_slab_page_t*) p;
pool->pages->slab = pages;
pool->pages->next = &pool->free;
pool->pages->prev = (uintptr_t) &pool->free;
pool->start = (u_char*)
ncx_align_ptr((uintptr_t) p + pages * sizeof(ncx_slab_page_t),
ncx_pagesize);
ncx_real_pages = (pool->end - pool->start) / ncx_pagesize;
pool->pages->slab = ncx_real_pages;
}
void* ncx_slab_alloc(ncx_slab_pool_t* pool, size_t size) {
ncx_shmtx_lock(&pool->mutex);
void* p = ncx_slab_alloc_locked(pool, size);
ncx_shmtx_unlock(&pool->mutex);
return p;
}
void* ncx_slab_alloc_locked(ncx_slab_pool_t* pool, size_t size) {
size_t s;
uintptr_t p, n, m, mask,* bitmap;
ncx_uint_t i, slot, shift, map;
ncx_slab_page_t* page,* prev,* slots;
if (size >= ncx_slab_max_size) {
// debug("slab alloc: %zu", size);
page = ncx_slab_alloc_pages(pool, (size >> ncx_pagesize_shift)
+ ((size % ncx_pagesize) ? 1 : 0));
if (page) {
p = (page - pool->pages) << ncx_pagesize_shift;
p += (uintptr_t) pool->start;
} else {
p = 0;
}
goto done;
}
if (size > pool->min_size) {
shift = 1;
for (s = size - 1; s >>= 1; shift++) {
/* void */
}
slot = shift - pool->min_shift;
} else {
size = pool->min_size;
shift = pool->min_shift;
slot = 0;
}
slots = (ncx_slab_page_t*) ((u_char*) pool + sizeof(ncx_slab_pool_t));
page = slots[slot].next;
if (page->next != page) {
if (shift < ncx_slab_exact_shift) {
do {
p = (page - pool->pages) << ncx_pagesize_shift;
bitmap = (uintptr_t*) (pool->start + p);
map = (1 << (ncx_pagesize_shift - shift))
/ (sizeof(uintptr_t) * 8);
for (n = 0; n < map; n++) {
if (bitmap[n] != NCX_SLAB_BUSY) {
for (m = 1, i = 0; m; m <<= 1, i++) {
if ((bitmap[n] & m)) {
continue;
}
bitmap[n] |= m;
i = ((n * sizeof(uintptr_t) * 8) << shift)
+ (i << shift);
if (bitmap[n] == NCX_SLAB_BUSY) {
for (n = n + 1; n < map; n++) {
if (bitmap[n] != NCX_SLAB_BUSY) {
p = (uintptr_t) bitmap + i;
goto done;
}
}
prev = (ncx_slab_page_t*)
(page->prev & ~NCX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev;
page->next = NULL;
page->prev = NCX_SLAB_SMALL;
}
p = (uintptr_t) bitmap + i;
goto done;
}
}
}
page = page->next;
} while (page);
} else if (shift == ncx_slab_exact_shift) {
do {
if (page->slab != NCX_SLAB_BUSY) {
for (m = 1, i = 0; m; m <<= 1, i++) {
if ((page->slab & m)) {
continue;
}
page->slab |= m;
if (page->slab == NCX_SLAB_BUSY) {
prev = (ncx_slab_page_t*)
(page->prev & ~NCX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev;
page->next = NULL;
page->prev = NCX_SLAB_EXACT;
}
p = (page - pool->pages) << ncx_pagesize_shift;
p += i << shift;
p += (uintptr_t) pool->start;
goto done;
}
}
page = page->next;
} while (page);
} else {
/* shift > ncx_slab_exact_shift */
n = ncx_pagesize_shift - (page->slab & NCX_SLAB_SHIFT_MASK);
n = 1 << n;
n = ((uintptr_t) 1 << n) - 1;
mask = n << NCX_SLAB_MAP_SHIFT;
do {
if ((page->slab & NCX_SLAB_MAP_MASK) != mask) {
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT, i = 0;
m & mask;
m <<= 1, i++) {
if ((page->slab & m)) {
continue;
}
page->slab |= m;
if ((page->slab & NCX_SLAB_MAP_MASK) == mask) {
prev = (ncx_slab_page_t*)
(page->prev & ~NCX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev;
page->next = NULL;
page->prev = NCX_SLAB_BIG;
}
p = (page - pool->pages) << ncx_pagesize_shift;
p += i << shift;
p += (uintptr_t) pool->start;
goto done;
}
}
page = page->next;
} while (page);
}
}
page = ncx_slab_alloc_pages(pool, 1);
if (page) {
if (shift < ncx_slab_exact_shift) {
p = (page - pool->pages) << ncx_pagesize_shift;
bitmap = (uintptr_t*) (pool->start + p);
s = 1 << shift;
n = (1 << (ncx_pagesize_shift - shift)) / 8 / s;
if (n == 0) {
n = 1;
}
bitmap[0] = (2 << n) - 1;
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
for (i = 1; i < map; i++) {
bitmap[i] = 0;
}
page->slab = shift;
page->next = &slots[slot];
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
slots[slot].next = page;
p = ((page - pool->pages) << ncx_pagesize_shift) + s * n;
p += (uintptr_t) pool->start;
goto done;
} else if (shift == ncx_slab_exact_shift) {
page->slab = 1;
page->next = &slots[slot];
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
slots[slot].next = page;
p = (page - pool->pages) << ncx_pagesize_shift;
p += (uintptr_t) pool->start;
goto done;
} else {
/* shift > ncx_slab_exact_shift */
page->slab = ((uintptr_t) 1 << NCX_SLAB_MAP_SHIFT) | shift;
page->next = &slots[slot];
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
slots[slot].next = page;
p = (page - pool->pages) << ncx_pagesize_shift;
p += (uintptr_t) pool->start;
goto done;
}
}
p = 0;
done:
// debug("slab alloc: %p", (void *)p);
return (void*) p;
}
void ncx_slab_free(ncx_slab_pool_t* pool, void* p) {
ncx_shmtx_lock(&pool->mutex);
ncx_slab_free_locked(pool, p);
ncx_shmtx_unlock(&pool->mutex);
}
void ncx_slab_free_locked(ncx_slab_pool_t* pool, void* p) {
size_t size;
uintptr_t slab, m,* bitmap;
ncx_uint_t n, type, slot, shift, map;
ncx_slab_page_t* slots,* page;
// debug("slab free: %p", p);
if ((u_char*) p < pool->start || (u_char*) p > pool->end) {
// error("ncx_slab_free(): outside of pool");
goto fail;
}
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
page = &pool->pages[n];
slab = page->slab;
type = page->prev & NCX_SLAB_PAGE_MASK;
switch (type) {
case NCX_SLAB_SMALL:
shift = slab & NCX_SLAB_SHIFT_MASK;
size = 1 << shift;
if ((uintptr_t) p & (size - 1)) {
goto wrong_chunk;
}
n = ((uintptr_t) p & (ncx_pagesize - 1)) >> shift;
m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));
n /= (sizeof(uintptr_t) * 8);
bitmap = (uintptr_t*) ((uintptr_t) p & ~(ncx_pagesize - 1));
if (bitmap[n] & m) {
if (page->next == NULL) {
slots = (ncx_slab_page_t*)
((u_char*) pool + sizeof(ncx_slab_pool_t));
slot = shift - pool->min_shift;
page->next = slots[slot].next;
slots[slot].next = page;
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
page->next->prev = (uintptr_t) page | NCX_SLAB_SMALL;
}
bitmap[n] &= ~m;
n = (1 << (ncx_pagesize_shift - shift)) / 8 / (1 << shift);
if (n == 0) {
n = 1;
}
if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) {
goto done;
}
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
for (n = 1; n < map; n++) {
if (bitmap[n]) {
goto done;
}
}
ncx_slab_free_pages(pool, page, 1);
goto done;
}
goto chunk_already_free;
case NCX_SLAB_EXACT:
m = (uintptr_t) 1 <<
(((uintptr_t) p & (ncx_pagesize - 1)) >> ncx_slab_exact_shift);
size = ncx_slab_exact_size;
if ((uintptr_t) p & (size - 1)) {
goto wrong_chunk;
}
if (slab & m) {
if (slab == NCX_SLAB_BUSY) {
slots = (ncx_slab_page_t*)
((u_char*) pool + sizeof(ncx_slab_pool_t));
slot = ncx_slab_exact_shift - pool->min_shift;
page->next = slots[slot].next;
slots[slot].next = page;
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
page->next->prev = (uintptr_t) page | NCX_SLAB_EXACT;
}
page->slab &= ~m;
if (page->slab) {
goto done;
}
ncx_slab_free_pages(pool, page, 1);
goto done;
}
goto chunk_already_free;
case NCX_SLAB_BIG:
shift = slab & NCX_SLAB_SHIFT_MASK;
size = 1 << shift;
if ((uintptr_t) p & (size - 1)) {
goto wrong_chunk;
}
m = (uintptr_t) 1 << ((((uintptr_t) p & (ncx_pagesize - 1)) >> shift)
+ NCX_SLAB_MAP_SHIFT);
if (slab & m) {
if (page->next == NULL) {
slots = (ncx_slab_page_t*)
((u_char*) pool + sizeof(ncx_slab_pool_t));
slot = shift - pool->min_shift;
page->next = slots[slot].next;
slots[slot].next = page;
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
page->next->prev = (uintptr_t) page | NCX_SLAB_BIG;
}
page->slab &= ~m;
if (page->slab & NCX_SLAB_MAP_MASK) {
goto done;
}
ncx_slab_free_pages(pool, page, 1);
goto done;
}
goto chunk_already_free;
case NCX_SLAB_PAGE:
if ((uintptr_t) p & (ncx_pagesize - 1)) {
goto wrong_chunk;
}
if (slab == NCX_SLAB_PAGE_FREE) {
// alert("ncx_slab_free(): page is already free");
goto fail;
}
if (slab == NCX_SLAB_PAGE_BUSY) {
// alert("ncx_slab_free(): pointer to wrong page");
goto fail;
}
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
size = slab & ~NCX_SLAB_PAGE_START;
ncx_slab_free_pages(pool, &pool->pages[n], size);
ncx_slab_junk(p, size << ncx_pagesize_shift);
return;
}
/* not reached */
return;
done:
ncx_slab_junk(p, size);
return;
wrong_chunk:
// error("ncx_slab_free(): pointer to wrong chunk");
goto fail;
chunk_already_free:
// error("ncx_slab_free(): chunk is already free");
fail:
return;
}
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool, ncx_uint_t pages) {
ncx_slab_page_t* page,* p;
for (page = pool->free.next; page != &pool->free; page = page->next) {
if (page->slab >= pages) {
if (page->slab > pages) {
page[pages].slab = page->slab - pages;
page[pages].next = page->next;
page[pages].prev = page->prev;
p = (ncx_slab_page_t*) page->prev;
p->next = &page[pages];
page->next->prev = (uintptr_t) &page[pages];
} else {
p = (ncx_slab_page_t*) page->prev;
p->next = page->next;
page->next->prev = page->prev;
}
page->slab = pages | NCX_SLAB_PAGE_START;
page->next = NULL;
page->prev = NCX_SLAB_PAGE;
if (--pages == 0) {
return page;
}
for (p = page + 1; pages; pages--) {
p->slab = NCX_SLAB_PAGE_BUSY;
p->next = NULL;
p->prev = NCX_SLAB_PAGE;
p++;
}
return page;
}
}
// error("ncx_slab_alloc() failed: no memory");
return NULL;
}
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
ncx_uint_t pages) {
ncx_slab_page_t* prev,* next;
if (pages > 1) {
ncx_memzero(&page[1], (pages - 1)* sizeof(ncx_slab_page_t));
}
if (page->next) {
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev;
}
page->slab = pages;
page->prev = (uintptr_t) &pool->free;
page->next = pool->free.next;
page->next->prev = (uintptr_t) page;
pool->free.next = page;
#ifdef PAGE_MERGE
if (pool->pages != page) {
prev = page - 1;
if (ncx_slab_empty(pool, prev)) {
for (; prev >= pool->pages; prev--) {
if (prev->slab != 0)
{
pool->free.next = page->next;
page->next->prev = (uintptr_t) &pool->free;
prev->slab += pages;
ncx_memzero(page, sizeof(ncx_slab_page_t));
page = prev;
break;
}
}
}
}
if ((page - pool->pages + page->slab) < ncx_real_pages) {
next = page + page->slab;
if (ncx_slab_empty(pool, next))
{
prev = (ncx_slab_page_t *) (next->prev);
prev->next = next->next;
next->next->prev = next->prev;
page->slab += next->slab;
ncx_memzero(next, sizeof(ncx_slab_page_t));
}
}
#endif
}
void ncx_slab_dummy_init(ncx_slab_pool_t* pool) {
ncx_uint_t n;
#ifdef __APPLE__
vm_size_t pagesize;
host_page_size(mach_host_self(), &pagesize);
ncx_pagesize = pagesize;
#else
ncx_pagesize = getpagesize();
#endif
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
n >>= 1; ncx_pagesize_shift++) {
/* void */
}
if (ncx_slab_max_size == 0) {
ncx_slab_max_size = ncx_pagesize / 2;
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
/* void */
}
}
}
void ncx_slab_stat(ncx_slab_pool_t* pool, ncx_slab_stat_t* stat) {
uintptr_t m, n, mask, slab;
uintptr_t* bitmap;
ncx_uint_t i, j, map, type, obj_size;
ncx_slab_page_t* page;
ncx_memzero(stat, sizeof(ncx_slab_stat_t));
page = pool->pages;
stat->pages = (pool->end - pool->start) / ncx_pagesize;;
for (i = 0; i < stat->pages; i++) {
slab = page->slab;
type = page->prev & NCX_SLAB_PAGE_MASK;
switch (type) {
case NCX_SLAB_SMALL:
n = (page - pool->pages) << ncx_pagesize_shift;
bitmap = (uintptr_t*) (pool->start + n);
obj_size = 1 << slab;
map = (1 << (ncx_pagesize_shift - slab))
/ (sizeof(uintptr_t) * 8);
for (j = 0; j < map; j++) {
for (m = 1; m; m <<= 1) {
if ((bitmap[j] & m)) {
stat->used_size += obj_size;
stat->b_small += obj_size;
}
}
}
stat->p_small++;
break;
case NCX_SLAB_EXACT:
if (slab == NCX_SLAB_BUSY) {
stat->used_size += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
stat->b_exact += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
} else {
for (m = 1; m; m <<= 1) {
if (slab & m) {
stat->used_size += ncx_slab_exact_size;
stat->b_exact += ncx_slab_exact_size;
}
}
}
stat->p_exact++;
break;
case NCX_SLAB_BIG:
j = ncx_pagesize_shift - (slab & NCX_SLAB_SHIFT_MASK);
j = 1 << j;
j = ((uintptr_t) 1 << j) - 1;
mask = j << NCX_SLAB_MAP_SHIFT;
obj_size = 1 << (slab & NCX_SLAB_SHIFT_MASK);
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT; m & mask; m <<= 1) {
if ((page->slab & m)) {
stat->used_size += obj_size;
stat->b_big += obj_size;
}
}
stat->p_big++;
break;
case NCX_SLAB_PAGE:
if (page->prev == NCX_SLAB_PAGE) {
slab = slab & ~NCX_SLAB_PAGE_START;
stat->used_size += slab * ncx_pagesize;
stat->b_page += slab * ncx_pagesize;
stat->p_page += slab;
i += (slab - 1);
break;
}
default:
if (slab > stat->max_free_pages) {
stat->max_free_pages = page->slab;
}
stat->free_page += slab;
i += (slab - 1);
break;
}
page = pool->pages + i + 1;
}
stat->pool_size = pool->end - pool->start;
stat->used_pct = stat->used_size * 100 / stat->pool_size;
// info("pool_size : %zu bytes", stat->pool_size);
// info("used_size : %zu bytes", stat->used_size);
// info("used_pct : %zu%%\n", stat->used_pct);
// info("total page count : %zu", stat->pages);
// info("free page count : %zu\n", stat->free_page);
// info("small slab use page : %zu,\tbytes : %zu", stat->p_small, stat->b_small);
// info("exact slab use page : %zu,\tbytes : %zu", stat->p_exact, stat->b_exact);
// info("big slab use page : %zu,\tbytes : %zu", stat->p_big, stat->b_big);
// info("page slab use page : %zu,\tbytes : %zu\n", stat->p_page, stat->b_page);
// info("max free pages : %zu\n", stat->max_free_pages);
}
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page) {
ncx_slab_page_t* prev;
if (page->slab == 0) {
return true;
}
//page->prev == PAGE | SMALL | EXACT | BIG
if (page->next == NULL) {
return false;
}
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
while (prev >= pool->pages) {
prev = (ncx_slab_page_t*) (prev->prev & ~NCX_SLAB_PAGE_MASK);
};
if (prev == &pool->free) {
return true;
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
#pragma once
#include "ncx_core.h"
#include "ncx_lock.h"
#include "extern.h"
typedef struct ncx_slab_page_s ncx_slab_page_t;
struct ncx_slab_page_s {
uintptr_t slab;
ncx_slab_page_t *next;
uintptr_t prev;
};
typedef struct {
size_t min_size;
size_t min_shift;
ncx_slab_page_t *pages;
ncx_slab_page_t free;
u_char *start;
u_char *end;
ncx_shmtx_t mutex;
void *addr;
} ncx_slab_pool_t;
typedef struct {
size_t pool_size, used_size, used_pct;
size_t pages, free_page;
size_t p_small, p_exact, p_big, p_page; /* 四种slab占用的page数 */
size_t b_small, b_exact, b_big, b_page; /* 四种slab占用的byte数 */
size_t max_free_pages; /* 最大的连续可用page数 */
} ncx_slab_stat_t;
#ifdef __cplusplus
extern "C" { // only need to export C interface if
// used by C++ source code
#endif
MEMPOOL_API void ncx_slab_init(ncx_slab_pool_t *pool);
MEMPOOL_API void* ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size);
MEMPOOL_API void* ncx_slab_alloc_locked(ncx_slab_pool_t *pool, size_t size);
MEMPOOL_API void ncx_slab_free(ncx_slab_pool_t *pool, void *p);
MEMPOOL_API void ncx_slab_free_locked(ncx_slab_pool_t *pool, void *p);
MEMPOOL_API void ncx_slab_dummy_init(ncx_slab_pool_t *pool);
MEMPOOL_API void ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat);
#ifdef __cplusplus
}
#endif

@ -1 +1 @@
Subproject commit fc27de3eae7d35ec3a7dea6ab37da786d7ccacdb
Subproject commit 598fa70633c10b2ae7354b976b4976bf40502405