Remember per-device bluetooth volume
This commit is contained in:
+41
-4
@@ -12,6 +12,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "audio_sink.hpp"
|
#include "audio_sink.hpp"
|
||||||
|
#include "bluetooth_types.hpp"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "freertos/portmacro.h"
|
#include "freertos/portmacro.h"
|
||||||
#include "freertos/projdefs.h"
|
#include "freertos/projdefs.h"
|
||||||
@@ -51,14 +52,25 @@ std::shared_ptr<IAudioOutput> AudioState::sOutput;
|
|||||||
std::optional<database::TrackId> AudioState::sCurrentTrack;
|
std::optional<database::TrackId> AudioState::sCurrentTrack;
|
||||||
bool AudioState::sIsPlaybackAllowed;
|
bool AudioState::sIsPlaybackAllowed;
|
||||||
|
|
||||||
void AudioState::react(const system_fsm::KeyLockChanged& ev) {
|
void AudioState::react(const system_fsm::BluetoothEvent& ev) {
|
||||||
if (ev.locking && sServices) {
|
if (ev.event != drivers::bluetooth::Event::kConnectionStateChanged) {
|
||||||
sServices->nvs().AmpCurrentVolume(sOutput->GetVolume());
|
return;
|
||||||
}
|
}
|
||||||
|
auto dev = sServices->bluetooth().ConnectedDevice();
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto vols = sServices->nvs().BluetoothVolumes();
|
||||||
|
sBtOutput->SetVolume(vols.Get(dev->mac).value_or(10));
|
||||||
|
events::Ui().Dispatch(VolumeChanged{
|
||||||
|
.percent = sOutput->GetVolumePct(),
|
||||||
|
.db = sOutput->GetVolumeDb(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioState::react(const StepUpVolume& ev) {
|
void AudioState::react(const StepUpVolume& ev) {
|
||||||
if (sOutput->AdjustVolumeUp()) {
|
if (sOutput->AdjustVolumeUp()) {
|
||||||
|
commitVolume();
|
||||||
events::Ui().Dispatch(VolumeChanged{
|
events::Ui().Dispatch(VolumeChanged{
|
||||||
.percent = sOutput->GetVolumePct(),
|
.percent = sOutput->GetVolumePct(),
|
||||||
.db = sOutput->GetVolumeDb(),
|
.db = sOutput->GetVolumeDb(),
|
||||||
@@ -68,6 +80,7 @@ void AudioState::react(const StepUpVolume& ev) {
|
|||||||
|
|
||||||
void AudioState::react(const StepDownVolume& ev) {
|
void AudioState::react(const StepDownVolume& ev) {
|
||||||
if (sOutput->AdjustVolumeDown()) {
|
if (sOutput->AdjustVolumeDown()) {
|
||||||
|
commitVolume();
|
||||||
events::Ui().Dispatch(VolumeChanged{
|
events::Ui().Dispatch(VolumeChanged{
|
||||||
.percent = sOutput->GetVolumePct(),
|
.percent = sOutput->GetVolumePct(),
|
||||||
.db = sOutput->GetVolumeDb(),
|
.db = sOutput->GetVolumeDb(),
|
||||||
@@ -126,6 +139,14 @@ void AudioState::react(const OutputModeChanged& ev) {
|
|||||||
}
|
}
|
||||||
sOutput->SetMode(IAudioOutput::Modes::kOnPaused);
|
sOutput->SetMode(IAudioOutput::Modes::kOnPaused);
|
||||||
sSampleConverter->SetOutput(sOutput);
|
sSampleConverter->SetOutput(sOutput);
|
||||||
|
|
||||||
|
// Bluetooth volume isn't 'changed' until we've connected to a device.
|
||||||
|
if (new_mode == drivers::NvsStorage::Output::kHeadphones) {
|
||||||
|
events::Ui().Dispatch(VolumeChanged{
|
||||||
|
.percent = sOutput->GetVolumePct(),
|
||||||
|
.db = sOutput->GetVolumeDb(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AudioState::playTrack(database::TrackId id) -> void {
|
auto AudioState::playTrack(database::TrackId id) -> void {
|
||||||
@@ -139,6 +160,22 @@ auto AudioState::playTrack(database::TrackId id) -> void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto AudioState::commitVolume() -> void {
|
||||||
|
auto mode = sServices->nvs().OutputMode();
|
||||||
|
auto vol = sOutput->GetVolume();
|
||||||
|
if (mode == drivers::NvsStorage::Output::kHeadphones) {
|
||||||
|
sServices->nvs().AmpCurrentVolume(vol);
|
||||||
|
} else if (mode == drivers::NvsStorage::Output::kBluetooth) {
|
||||||
|
auto dev = sServices->bluetooth().ConnectedDevice();
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto vols = sServices->nvs().BluetoothVolumes();
|
||||||
|
vols.Put(dev->mac, vol);
|
||||||
|
sServices->nvs().BluetoothVolumes(vols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto AudioState::readyToPlay() -> bool {
|
auto AudioState::readyToPlay() -> bool {
|
||||||
return sCurrentTrack.has_value() && sIsPlaybackAllowed;
|
return sCurrentTrack.has_value() && sIsPlaybackAllowed;
|
||||||
}
|
}
|
||||||
@@ -283,7 +320,7 @@ void Playback::exit() {
|
|||||||
|
|
||||||
// Stash the current volume now, in case it changed during playback, since we
|
// Stash the current volume now, in case it changed during playback, since we
|
||||||
// might be powering off soon.
|
// might be powering off soon.
|
||||||
sServices->nvs().AmpCurrentVolume(sOutput->GetVolume());
|
commitVolume();
|
||||||
|
|
||||||
events::System().Dispatch(PlaybackStopped{});
|
events::System().Dispatch(PlaybackStopped{});
|
||||||
events::Ui().Dispatch(PlaybackStopped{});
|
events::Ui().Dispatch(PlaybackStopped{});
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
|
|||||||
void react(const OutputModeChanged&);
|
void react(const OutputModeChanged&);
|
||||||
|
|
||||||
virtual void react(const system_fsm::BootComplete&) {}
|
virtual void react(const system_fsm::BootComplete&) {}
|
||||||
virtual void react(const system_fsm::KeyLockChanged&);
|
virtual void react(const system_fsm::KeyLockChanged&) {};
|
||||||
virtual void react(const system_fsm::StorageMounted&) {}
|
virtual void react(const system_fsm::StorageMounted&) {}
|
||||||
|
virtual void react(const system_fsm::BluetoothEvent&);
|
||||||
|
|
||||||
virtual void react(const PlayFile&) {}
|
virtual void react(const PlayFile&) {}
|
||||||
virtual void react(const QueueUpdate&) {}
|
virtual void react(const QueueUpdate&) {}
|
||||||
@@ -67,6 +68,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
auto playTrack(database::TrackId id) -> void;
|
auto playTrack(database::TrackId id) -> void;
|
||||||
|
auto commitVolume() -> void;
|
||||||
|
|
||||||
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
|
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
|
||||||
|
|
||||||
@@ -88,6 +90,9 @@ namespace states {
|
|||||||
class Uninitialised : public AudioState {
|
class Uninitialised : public AudioState {
|
||||||
public:
|
public:
|
||||||
void react(const system_fsm::BootComplete&) override;
|
void react(const system_fsm::BootComplete&) override;
|
||||||
|
|
||||||
|
void react(const system_fsm::BluetoothEvent&) override {};
|
||||||
|
|
||||||
using AudioState::react;
|
using AudioState::react;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ idf_component_register(
|
|||||||
"spiffs.cpp"
|
"spiffs.cpp"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks" "nvs_flash" "spiffs"
|
REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks" "nvs_flash" "spiffs"
|
||||||
"bt" "tinyfsm")
|
"bt" "tinyfsm" "util")
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
|
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
|
||||||
|
|||||||
@@ -103,21 +103,12 @@ auto Bluetooth::IsConnected() -> bool {
|
|||||||
return bluetooth::BluetoothState::is_in_state<bluetooth::Connected>();
|
return bluetooth::BluetoothState::is_in_state<bluetooth::Connected>();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bluetooth::ConnectedDevice() -> std::optional<bluetooth::Device> {
|
auto Bluetooth::ConnectedDevice() -> std::optional<bluetooth::MacAndName> {
|
||||||
auto lock = bluetooth::BluetoothState::lock();
|
auto lock = bluetooth::BluetoothState::lock();
|
||||||
if (!bluetooth::BluetoothState::is_in_state<bluetooth::Connected>()) {
|
if (!bluetooth::BluetoothState::is_in_state<bluetooth::Connected>()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto looking_for = bluetooth::BluetoothState::preferred_device();
|
return bluetooth::BluetoothState::preferred_device();
|
||||||
if (!looking_for) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
for (const auto& dev : bluetooth::BluetoothState::devices()) {
|
|
||||||
if (dev.address == looking_for->mac) {
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bluetooth::KnownDevices() -> std::vector<bluetooth::Device> {
|
auto Bluetooth::KnownDevices() -> std::vector<bluetooth::Device> {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class Bluetooth {
|
|||||||
auto IsEnabled() -> bool;
|
auto IsEnabled() -> bool;
|
||||||
|
|
||||||
auto IsConnected() -> bool;
|
auto IsConnected() -> bool;
|
||||||
auto ConnectedDevice() -> std::optional<bluetooth::Device>;
|
auto ConnectedDevice() -> std::optional<bluetooth::MacAndName>;
|
||||||
|
|
||||||
auto KnownDevices() -> std::vector<bluetooth::Device>;
|
auto KnownDevices() -> std::vector<bluetooth::Device>;
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "bluetooth_types.hpp"
|
#include "bluetooth_types.hpp"
|
||||||
#include "tasks.hpp"
|
#include "tasks.hpp"
|
||||||
|
#include "lru_cache.hpp"
|
||||||
|
|
||||||
namespace drivers {
|
namespace drivers {
|
||||||
|
|
||||||
@@ -28,6 +29,11 @@ class NvsStorage {
|
|||||||
auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>;
|
auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>;
|
||||||
auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> bool;
|
auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> bool;
|
||||||
|
|
||||||
|
using BtVolumes = util::LruCache<10, bluetooth::mac_addr_t, uint8_t> ;
|
||||||
|
|
||||||
|
auto BluetoothVolumes() -> BtVolumes;
|
||||||
|
auto BluetoothVolumes(const BtVolumes&) -> bool;
|
||||||
|
|
||||||
enum class Output : uint8_t {
|
enum class Output : uint8_t {
|
||||||
kHeadphones = 0,
|
kHeadphones = 0,
|
||||||
kBluetooth = 1,
|
kBluetooth = 1,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "bluetooth.hpp"
|
#include "bluetooth.hpp"
|
||||||
#include "bluetooth_types.hpp"
|
#include "bluetooth_types.hpp"
|
||||||
|
#include "cppbor.h"
|
||||||
|
#include "cppbor_parse.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
@@ -27,6 +29,7 @@ static constexpr uint8_t kSchemaVersion = 1;
|
|||||||
static constexpr char kKeyVersion[] = "ver";
|
static constexpr char kKeyVersion[] = "ver";
|
||||||
static constexpr char kKeyBluetoothMac[] = "bt_mac";
|
static constexpr char kKeyBluetoothMac[] = "bt_mac";
|
||||||
static constexpr char kKeyBluetoothName[] = "bt_name";
|
static constexpr char kKeyBluetoothName[] = "bt_name";
|
||||||
|
static constexpr char kKeyBluetoothVolumes[] = "bt_vols";
|
||||||
static constexpr char kKeyOutput[] = "out";
|
static constexpr char kKeyOutput[] = "out";
|
||||||
static constexpr char kKeyBrightness[] = "bright";
|
static constexpr char kKeyBrightness[] = "bright";
|
||||||
static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max";
|
static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max";
|
||||||
@@ -135,6 +138,101 @@ auto NvsStorage::PreferredBluetoothDevice(
|
|||||||
return nvs_commit(handle_) == ESP_OK;
|
return nvs_commit(handle_) == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VolumesParseClient : public cppbor::ParseClient {
|
||||||
|
public:
|
||||||
|
VolumesParseClient(NvsStorage::BtVolumes& out)
|
||||||
|
: state_(State::kInit), mac_(), vol_(), out_(out) {}
|
||||||
|
|
||||||
|
ParseClient* item(std::unique_ptr<cppbor::Item>& item,
|
||||||
|
const uint8_t* hdrBegin,
|
||||||
|
const uint8_t* valueBegin,
|
||||||
|
const uint8_t* end) override {
|
||||||
|
if (item->type() == cppbor::ARRAY) {
|
||||||
|
if (state_ == State::kInit) {
|
||||||
|
ESP_LOGI(kTag, "enter root");
|
||||||
|
state_ = State::kRoot;
|
||||||
|
} else if (state_ == State::kRoot) {
|
||||||
|
ESP_LOGI(kTag, "enter pair");
|
||||||
|
state_ = State::kPair;
|
||||||
|
}
|
||||||
|
} else if (item->type() == cppbor::BSTR && state_ == State::kPair) {
|
||||||
|
ESP_LOGI(kTag, "get str");
|
||||||
|
auto data = item->asBstr()->value();
|
||||||
|
mac_.emplace();
|
||||||
|
std::copy(data.begin(), data.end(), mac_->begin());
|
||||||
|
} else if (item->type() == cppbor::UINT && state_ == State::kPair) {
|
||||||
|
vol_ =
|
||||||
|
std::clamp<uint64_t>(item->asUint()->unsignedValue(), 0, UINT8_MAX);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseClient* itemEnd(std::unique_ptr<cppbor::Item>& item,
|
||||||
|
const uint8_t* hdrBegin,
|
||||||
|
const uint8_t* valueBegin,
|
||||||
|
const uint8_t* end) override {
|
||||||
|
if (item->type() == cppbor::ARRAY) {
|
||||||
|
if (state_ == State::kRoot) {
|
||||||
|
state_ = State::kFinished;
|
||||||
|
} else if (state_ == State::kPair) {
|
||||||
|
if (vol_ && mac_) {
|
||||||
|
out_.Put(*mac_, *vol_);
|
||||||
|
}
|
||||||
|
mac_.reset();
|
||||||
|
vol_.reset();
|
||||||
|
state_ = State::kRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(const uint8_t* position,
|
||||||
|
const std::string& errorMessage) override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class State {
|
||||||
|
kInit,
|
||||||
|
kRoot,
|
||||||
|
kPair,
|
||||||
|
kFinished,
|
||||||
|
};
|
||||||
|
|
||||||
|
State state_;
|
||||||
|
std::optional<bluetooth::mac_addr_t> mac_;
|
||||||
|
std::optional<uint8_t> vol_;
|
||||||
|
NvsStorage::BtVolumes& out_;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto NvsStorage::BluetoothVolumes() -> BtVolumes {
|
||||||
|
BtVolumes out;
|
||||||
|
size_t encoded_len = 0;
|
||||||
|
if (nvs_get_str(handle_, kKeyBluetoothVolumes, NULL, &encoded_len) !=
|
||||||
|
ESP_OK) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
auto encoded = std::unique_ptr<char[]>{new char[encoded_len]};
|
||||||
|
if (nvs_get_str(handle_, kKeyBluetoothVolumes, encoded.get(), &encoded_len) !=
|
||||||
|
ESP_OK) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
VolumesParseClient client{out};
|
||||||
|
auto data = reinterpret_cast<const uint8_t*>(encoded.get());
|
||||||
|
cppbor::parse(data, data + encoded_len, &client);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto NvsStorage::BluetoothVolumes(const BtVolumes& vols) -> bool {
|
||||||
|
cppbor::Array enc;
|
||||||
|
auto vols_list = vols.Get();
|
||||||
|
for (auto vol = vols_list.rbegin(); vol < vols_list.rend(); vol++) {
|
||||||
|
enc.add(cppbor::Array{cppbor::Bstr{{vol->first.data(), vol->first.size()}},
|
||||||
|
cppbor::Uint{vol->second}});
|
||||||
|
}
|
||||||
|
std::string encoded = enc.toString();
|
||||||
|
nvs_set_str(handle_, kKeyBluetoothVolumes, encoded.c_str());
|
||||||
|
return nvs_commit(handle_) == ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
auto NvsStorage::OutputMode() -> Output {
|
auto NvsStorage::OutputMode() -> Output {
|
||||||
uint8_t out = 0;
|
uint8_t out = 0;
|
||||||
nvs_get_u8(handle_, kKeyOutput, &out);
|
nvs_get_u8(handle_, kKeyOutput, &out);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace states {
|
|||||||
|
|
||||||
static auto bt_event_cb(drivers::bluetooth::Event ev) -> void {
|
static auto bt_event_cb(drivers::bluetooth::Event ev) -> void {
|
||||||
events::Ui().Dispatch(BluetoothEvent{.event = ev});
|
events::Ui().Dispatch(BluetoothEvent{.event = ev});
|
||||||
|
events::Audio().Dispatch(BluetoothEvent{.event = ev});
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TickType_t kInterruptCheckPeriod = pdMS_TO_TICKS(100);
|
static const TickType_t kInterruptCheckPeriod = pdMS_TO_TICKS(100);
|
||||||
|
|||||||
+8
-5
@@ -326,14 +326,20 @@ void UiState::react(const audio::VolumeLimitChanged& ev) {
|
|||||||
|
|
||||||
void UiState::react(const system_fsm::BluetoothEvent& ev) {
|
void UiState::react(const system_fsm::BluetoothEvent& ev) {
|
||||||
auto bt = sServices->bluetooth();
|
auto bt = sServices->bluetooth();
|
||||||
|
auto dev = bt.ConnectedDevice();
|
||||||
switch (ev.event) {
|
switch (ev.event) {
|
||||||
case drivers::bluetooth::Event::kKnownDevicesChanged:
|
case drivers::bluetooth::Event::kKnownDevicesChanged:
|
||||||
sBluetoothDevices.Update(bt.KnownDevices());
|
sBluetoothDevices.Update(bt.KnownDevices());
|
||||||
break;
|
break;
|
||||||
case drivers::bluetooth::Event::kConnectionStateChanged:
|
case drivers::bluetooth::Event::kConnectionStateChanged:
|
||||||
sBluetoothConnected.Update(bt.IsConnected());
|
sBluetoothConnected.Update(bt.IsConnected());
|
||||||
if (bt.ConnectedDevice()) {
|
if (dev) {
|
||||||
sBluetoothPairedDevice.Update(bt.ConnectedDevice().value());
|
sBluetoothPairedDevice.Update(drivers::bluetooth::Device{
|
||||||
|
.address = dev->mac,
|
||||||
|
.name = {dev->name.data(), dev->name.size()},
|
||||||
|
.class_of_device = 0,
|
||||||
|
.signal_strength = 0,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
sBluetoothPairedDevice.Update(std::monostate{});
|
sBluetoothPairedDevice.Update(std::monostate{});
|
||||||
}
|
}
|
||||||
@@ -457,9 +463,6 @@ void Lua::entry() {
|
|||||||
auto bt = sServices->bluetooth();
|
auto bt = sServices->bluetooth();
|
||||||
sBluetoothEnabled.Update(bt.IsEnabled());
|
sBluetoothEnabled.Update(bt.IsEnabled());
|
||||||
sBluetoothConnected.Update(bt.IsConnected());
|
sBluetoothConnected.Update(bt.IsConnected());
|
||||||
if (bt.ConnectedDevice()) {
|
|
||||||
sBluetoothPairedDevice.Update(bt.ConnectedDevice().value());
|
|
||||||
}
|
|
||||||
sBluetoothDevices.Update(bt.KnownDevices());
|
sBluetoothDevices.Update(bt.KnownDevices());
|
||||||
|
|
||||||
sCurrentScreen.reset();
|
sCurrentScreen.reset();
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "memory_resource.hpp"
|
#include "memory_resource.hpp"
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
@@ -64,9 +66,16 @@ class LruCache {
|
|||||||
key_to_it_.clear();
|
key_to_it_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Get() const -> std::vector<std::pair<K, V>> {
|
||||||
|
std::vector<std::pair<K, V>> out;
|
||||||
|
out.resize(entries_.size());
|
||||||
|
std::copy(entries_.begin(), entries_.end(), out.begin());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pmr::list<std::pair<K, V>> entries_;
|
std::pmr::list<std::pair<K, V>> entries_;
|
||||||
std::pmr::unordered_map<K, decltype(entries_.begin())> key_to_it_;
|
std::pmr::map<K, decltype(entries_.begin())> key_to_it_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|||||||
Reference in New Issue
Block a user