diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-10-13 15:05:49 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-10-13 15:05:49 +1100 |
| commit | afbf3c31f4d1a605c264f719531f4183ee5a3022 (patch) | |
| tree | 43c75029ff6dfe3e44137f6b3d0de3498f247bf2 /src/database | |
| parent | 20d1c280a77eadcea18438453dc37daaf1d85e2d (diff) | |
| download | tangara-fw-afbf3c31f4d1a605c264f719531f4183ee5a3022.tar.gz | |
Use libcppbor for much much nicer db encoding
Diffstat (limited to 'src/database')
| -rw-r--r-- | src/database/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/database/database.cpp | 67 | ||||
| -rw-r--r-- | src/database/include/records.hpp | 31 | ||||
| -rw-r--r-- | src/database/include/track.hpp | 5 | ||||
| -rw-r--r-- | src/database/index.cpp | 2 | ||||
| -rw-r--r-- | src/database/records.cpp | 372 | ||||
| -rw-r--r-- | src/database/track.cpp | 6 |
7 files changed, 125 insertions, 360 deletions
diff --git a/src/database/CMakeLists.txt b/src/database/CMakeLists.txt index dffc3a26..26c14815 100644 --- a/src/database/CMakeLists.txt +++ b/src/database/CMakeLists.txt @@ -7,7 +7,7 @@ idf_component_register( "file_gatherer.cpp" "tag_parser.cpp" "index.cpp" INCLUDE_DIRS "include" REQUIRES "result" "span" "esp_psram" "fatfs" "libtags" "komihash" "cbor" - "tasks" "memory" "util" "tinyfsm" "events" "opusfile") + "tasks" "memory" "util" "tinyfsm" "events" "opusfile" "libcppbor") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/database/database.cpp b/src/database/database.cpp index 9d29aea8..60aa69fc 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -126,9 +126,9 @@ auto Database::Update() -> std::future<void> { ESP_LOGI(kTag, "dropping stale indexes"); { std::unique_ptr<leveldb::Iterator> it{db_->NewIterator(read_options)}; - OwningSlice prefix = EncodeAllIndexesPrefix(); - it->Seek(prefix.slice); - while (it->Valid() && it->key().starts_with(prefix.slice)) { + std::string prefix = EncodeAllIndexesPrefix(); + it->Seek(prefix); + while (it->Valid() && it->key().starts_with(prefix)) { db_->Delete(leveldb::WriteOptions(), it->key()); it->Next(); } @@ -139,9 +139,9 @@ auto Database::Update() -> std::future<void> { { uint64_t num_processed = 0; std::unique_ptr<leveldb::Iterator> it{db_->NewIterator(read_options)}; - OwningSlice prefix = EncodeDataPrefix(); - it->Seek(prefix.slice); - while (it->Valid() && it->key().starts_with(prefix.slice)) { + std::string prefix = EncodeDataPrefix(); + it->Seek(prefix); + while (it->Valid() && it->key().starts_with(prefix)) { num_processed++; events::Ui().Dispatch(event::UpdateProgress{ .stage = event::UpdateProgress::Stage::kVerifyingExistingTracks, @@ -215,10 +215,10 @@ auto Database::Update() -> std::future<void> { // Check for any existing record with the same hash. uint64_t hash = tags->Hash(); - OwningSlice key = EncodeHashKey(hash); + std::string key = EncodeHashKey(hash); std::optional<TrackId> existing_hash; std::string raw_entry; - if (db_->Get(leveldb::ReadOptions(), key.slice, &raw_entry).ok()) { + if (db_->Get(leveldb::ReadOptions(), key, &raw_entry).ok()) { existing_hash = ParseHashValue(raw_entry); } @@ -306,9 +306,9 @@ auto Database::GetBulkTracks(std::vector<TrackId> ids) std::unique_ptr<leveldb::Iterator> it{ db_->NewIterator(leveldb::ReadOptions{})}; for (const TrackId& id : sorted_ids) { - OwningSlice key = EncodeDataKey(id); - it->Seek(key.slice); - if (!it->Valid() || it->key() != key.slice) { + std::string key = EncodeDataKey(id); + it->Seek(key); + if (!it->Valid() || it->key() != key) { // This id wasn't found at all. Skip it. continue; } @@ -354,9 +354,9 @@ auto Database::GetTracksByIndex(const IndexInfo& index, std::size_t page_size) .depth = 0, .components_hash = 0, }; - OwningSlice prefix = EncodeIndexPrefix(header); - Continuation c{.prefix = prefix.data, - .start_key = prefix.data, + std::string prefix = EncodeIndexPrefix(header); + Continuation c{.prefix = {prefix.data(), prefix.size()}, + .start_key = {prefix.data(), prefix.size()}, .forward = true, .was_prev_forward = true, .page_size = page_size}; @@ -366,8 +366,9 @@ auto Database::GetTracksByIndex(const IndexInfo& index, std::size_t page_size) auto Database::GetTracks(std::size_t page_size) -> std::future<Result<Track>*> { return worker_task_->Dispatch<Result<Track>*>([=, this]() -> Result<Track>* { - Continuation c{.prefix = EncodeDataPrefix().data, - .start_key = EncodeDataPrefix().data, + std::string prefix = EncodeDataPrefix(); + Continuation c{.prefix = {prefix.data(), prefix.size()}, + .start_key = {prefix.data(), prefix.size()}, .forward = true, .was_prev_forward = true, .page_size = page_size}; @@ -414,7 +415,7 @@ auto Database::dbMintNewTrackId() -> TrackId { } if (!db_->Put(leveldb::WriteOptions(), kTrackIdKey, - TrackIdToBytes(next_id + 1).slice) + TrackIdToBytes(next_id + 1)) .ok()) { ESP_LOGE(kTag, "failed to write next track id"); } @@ -423,25 +424,25 @@ auto Database::dbMintNewTrackId() -> TrackId { } auto Database::dbEntomb(TrackId id, uint64_t hash) -> void { - OwningSlice key = EncodeHashKey(hash); - OwningSlice val = EncodeHashValue(id); - if (!db_->Put(leveldb::WriteOptions(), key.slice, val.slice).ok()) { + std::string key = EncodeHashKey(hash); + std::string val = EncodeHashValue(id); + if (!db_->Put(leveldb::WriteOptions(), key, val).ok()) { ESP_LOGE(kTag, "failed to entomb #%llx (id #%lx)", hash, id); } } auto Database::dbPutTrackData(const TrackData& s) -> void { - OwningSlice key = EncodeDataKey(s.id()); - OwningSlice val = EncodeDataValue(s); - if (!db_->Put(leveldb::WriteOptions(), key.slice, val.slice).ok()) { + std::string key = EncodeDataKey(s.id()); + std::string val = EncodeDataValue(s); + if (!db_->Put(leveldb::WriteOptions(), key, val).ok()) { ESP_LOGE(kTag, "failed to write data for #%lx", s.id()); } } auto Database::dbGetTrackData(TrackId id) -> std::shared_ptr<TrackData> { - OwningSlice key = EncodeDataKey(id); + std::string key = EncodeDataKey(id); std::string raw_val; - if (!db_->Get(leveldb::ReadOptions(), key.slice, &raw_val).ok()) { + if (!db_->Get(leveldb::ReadOptions(), key, &raw_val).ok()) { ESP_LOGW(kTag, "no key found for #%lx", id); return {}; } @@ -449,17 +450,17 @@ auto Database::dbGetTrackData(TrackId id) -> std::shared_ptr<TrackData> { } auto Database::dbPutHash(const uint64_t& hash, TrackId i) -> void { - OwningSlice key = EncodeHashKey(hash); - OwningSlice val = EncodeHashValue(i); - if (!db_->Put(leveldb::WriteOptions(), key.slice, val.slice).ok()) { + std::string key = EncodeHashKey(hash); + std::string val = EncodeHashValue(i); + if (!db_->Put(leveldb::WriteOptions(), key, val).ok()) { ESP_LOGE(kTag, "failed to write hash for #%lx", i); } } auto Database::dbGetHash(const uint64_t& hash) -> std::optional<TrackId> { - OwningSlice key = EncodeHashKey(hash); + std::string key = EncodeHashKey(hash); std::string raw_val; - if (!db_->Get(leveldb::ReadOptions(), key.slice, &raw_val).ok()) { + if (!db_->Get(leveldb::ReadOptions(), key, &raw_val).ok()) { ESP_LOGW(kTag, "no key found for hash #%llx", hash); return {}; } @@ -672,10 +673,10 @@ auto IndexRecord::Expand(std::size_t page_size) const return {}; } IndexKey::Header new_header = ExpandHeader(key_.header, key_.item); - OwningSlice new_prefix = EncodeIndexPrefix(new_header); + std::string new_prefix = EncodeIndexPrefix(new_header); return Continuation{ - .prefix = new_prefix.data, - .start_key = new_prefix.data, + .prefix = {new_prefix.data(), new_prefix.size()}, + .start_key = {new_prefix.data(), new_prefix.size()}, .forward = true, .was_prev_forward = true, .page_size = page_size, diff --git a/src/database/include/records.hpp b/src/database/include/records.hpp index e7d7738c..e13c6568 100644 --- a/src/database/include/records.hpp +++ b/src/database/include/records.hpp @@ -22,32 +22,19 @@ namespace database { /* - * Helper class for creating leveldb Slices bundled with the data they point to. - * Slices are otherwise non-owning, which can make handling them post-creation - * difficult. - */ -class OwningSlice { - public: - std::pmr::string data; - leveldb::Slice slice; - - explicit OwningSlice(std::pmr::string d); -}; - -/* * Returns the prefix added to every TrackData key. This can be used to iterate * over every data record in the database. */ -auto EncodeDataPrefix() -> OwningSlice; +auto EncodeDataPrefix() -> std::string; /* Encodes a data key for a track with the specified id. */ -auto EncodeDataKey(const TrackId& id) -> OwningSlice; +auto EncodeDataKey(const TrackId& id) -> std::string; /* * Encodes a TrackData instance into bytes, in preparation for storing it within * the database. This encoding is consistent, and will remain stable over time. */ -auto EncodeDataValue(const TrackData& track) -> OwningSlice; +auto EncodeDataValue(const TrackData& track) -> std::string; /* * Parses bytes previously encoded via EncodeDataValue back into a TrackData. @@ -56,14 +43,14 @@ auto EncodeDataValue(const TrackData& track) -> OwningSlice; auto ParseDataValue(const leveldb::Slice& slice) -> std::shared_ptr<TrackData>; /* Encodes a hash key for the specified hash. */ -auto EncodeHashKey(const uint64_t& hash) -> OwningSlice; +auto EncodeHashKey(const uint64_t& hash) -> std::string; /* * Encodes a hash value (at this point just a track id) into bytes, in * preparation for storing within the database. This encoding is consistent, and * will remain stable over time. */ -auto EncodeHashValue(TrackId id) -> OwningSlice; +auto EncodeHashValue(TrackId id) -> std::string; /* * Parses bytes previously encoded via EncodeHashValue back into a track id. May @@ -72,17 +59,17 @@ auto EncodeHashValue(TrackId id) -> OwningSlice; auto ParseHashValue(const leveldb::Slice&) -> std::optional<TrackId>; /* Encodes a prefix that matches all index keys, of all ids and depths. */ -auto EncodeAllIndexesPrefix() -> OwningSlice; +auto EncodeAllIndexesPrefix() -> std::string; /* */ -auto EncodeIndexPrefix(const IndexKey::Header&) -> OwningSlice; +auto EncodeIndexPrefix(const IndexKey::Header&) -> std::string; -auto EncodeIndexKey(const IndexKey&) -> OwningSlice; +auto EncodeIndexKey(const IndexKey&) -> std::string; auto ParseIndexKey(const leveldb::Slice&) -> std::optional<IndexKey>; /* Encodes a TrackId as bytes. */ -auto TrackIdToBytes(TrackId id) -> OwningSlice; +auto TrackIdToBytes(TrackId id) -> std::string; /* * Converts a track id encoded via TrackIdToBytes back into a TrackId. May diff --git a/src/database/include/track.hpp b/src/database/include/track.hpp index 3c7b20fa..44bfbc54 100644 --- a/src/database/include/track.hpp +++ b/src/database/include/track.hpp @@ -117,7 +117,6 @@ class TrackData { const TrackId id_; const std::pmr::string filepath_; const uint64_t tags_hash_; - const uint32_t play_count_; const bool is_tombstoned_; public: @@ -126,18 +125,15 @@ class TrackData { : id_(id), filepath_(path, &memory::kSpiRamResource), tags_hash_(hash), - play_count_(0), is_tombstoned_(false) {} TrackData(TrackId id, const std::pmr::string& path, uint64_t hash, - uint32_t play_count, bool is_tombstoned) : id_(id), filepath_(path, &memory::kSpiRamResource), tags_hash_(hash), - play_count_(play_count), is_tombstoned_(is_tombstoned) {} TrackData(TrackData&& other) = delete; @@ -147,7 +143,6 @@ class TrackData { auto id() const -> TrackId { return id_; } auto filepath() const -> std::pmr::string { return filepath_; } - auto play_count() const -> uint32_t { return play_count_; } auto tags_hash() const -> uint64_t { return tags_hash_; } auto is_tombstoned() const -> bool { return is_tombstoned_; } diff --git a/src/database/index.cpp b/src/database/index.cpp index 79939aaa..c7fd753a 100644 --- a/src/database/index.cpp +++ b/src/database/index.cpp @@ -94,7 +94,7 @@ auto Index(const IndexInfo& info, const Track& t, leveldb::WriteBatch* batch) } auto encoded = EncodeIndexKey(key); - batch->Put(encoded.slice, {value.data(), value.size()}); + batch->Put(encoded, {value.data(), value.size()}); // If there are more components after this, then we need to finish by // narrowing the header with the current title. diff --git a/src/database/records.cpp b/src/database/records.cpp index 103b3547..37ea429b 100644 --- a/src/database/records.cpp +++ b/src/database/records.cpp @@ -7,6 +7,7 @@ #include "records.hpp" #include <stdint.h> +#include <sys/_stdint.h> #include <functional> #include <iomanip> @@ -15,7 +16,8 @@ #include <string> #include <vector> -#include "cbor.h" +#include "cppbor.h" +#include "cppbor_parse.h" #include "esp_log.h" #include "index.hpp" @@ -49,244 +51,76 @@ static const char kHashPrefix = 'H'; static const char kIndexPrefix = 'I'; static const char kFieldSeparator = '\0'; -using ostringstream = - std::basic_ostringstream<char, - std::char_traits<char>, - std::pmr::polymorphic_allocator<char>>; - -/* - * Helper function for allocating an appropriately-sized byte buffer, then - * encoding data into it. - * - * 'T' should be a callable that takes a CborEncoder* as - * an argument, and stores values within that encoder. 'T' will be called - * exactly twice: first to detemine the buffer size, and then second to do the - * encoding. - * - * 'out_buf' will be set to the location of newly allocated memory; it is up to - * the caller to free it. Returns the size of 'out_buf'. - */ -template <typename T> -auto cbor_encode(uint8_t** out_buf, T fn) -> std::size_t { - // First pass: work out how many bytes we will encode into. - // FIXME: With benchmarking to help, we could consider preallocting a small - // buffer here to do the whole encoding in one pass. - CborEncoder size_encoder; - cbor_encoder_init(&size_encoder, NULL, 0, 0); - std::invoke(fn, &size_encoder); - std::size_t buf_size = cbor_encoder_get_extra_bytes_needed(&size_encoder); - - // Second pass: do the encoding. - CborEncoder encoder; - *out_buf = new uint8_t[buf_size]; - cbor_encoder_init(&encoder, *out_buf, buf_size, 0); - std::invoke(fn, &encoder); - - return buf_size; -} - -OwningSlice::OwningSlice(std::pmr::string d) - : data(d), slice(data.data(), data.size()) {} - /* 'D/' */ -auto EncodeDataPrefix() -> OwningSlice { - char data[2] = {kDataPrefix, kFieldSeparator}; - return OwningSlice({data, 2}); +auto EncodeDataPrefix() -> std::string { + return {kDataPrefix, kFieldSeparator}; } /* 'D/ 0xACAB' */ -auto EncodeDataKey(const TrackId& id) -> OwningSlice { - ostringstream output; - output.put(kDataPrefix).put(kFieldSeparator); - output << TrackIdToBytes(id).data; - return OwningSlice(output.str()); +auto EncodeDataKey(const TrackId& id) -> std::string { + return EncodeDataPrefix() + TrackIdToBytes(id); } -auto EncodeDataValue(const TrackData& track) -> OwningSlice { - uint8_t* buf; - std::size_t buf_len = cbor_encode(&buf, [&](CborEncoder* enc) { - CborEncoder array_encoder; - CborError err; - err = cbor_encoder_create_array(enc, &array_encoder, 5); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_int(&array_encoder, track.id()); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_text_string(&array_encoder, track.filepath().c_str(), - track.filepath().size()); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_uint(&array_encoder, track.tags_hash()); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_int(&array_encoder, track.play_count()); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_boolean(&array_encoder, track.is_tombstoned()); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encoder_close_container(enc, &array_encoder); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - }); - std::pmr::string as_str(reinterpret_cast<char*>(buf), buf_len); - delete buf; - return OwningSlice(as_str); +auto EncodeDataValue(const TrackData& track) -> std::string { + cppbor::Array val{ + cppbor::Uint{track.id()}, + cppbor::Tstr{track.filepath()}, + cppbor::Uint{track.tags_hash()}, + cppbor::Bool{track.is_tombstoned()}, + }; + return val.toString(); } auto ParseDataValue(const leveldb::Slice& slice) -> std::shared_ptr<TrackData> { - CborParser parser; - CborValue container; - CborError err; - err = cbor_parser_init(reinterpret_cast<const uint8_t*>(slice.data()), - slice.size(), 0, &parser, &container); - if (err != CborNoError || !cbor_value_is_container(&container)) { - return {}; - } - - CborValue val; - err = cbor_value_enter_container(&container, &val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - - uint64_t raw_int; - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - TrackId id = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_is_text_string(&val)) { + auto [item, unused, err] = cppbor::parseWithViews( + reinterpret_cast<const uint8_t*>(slice.data()), slice.size()); + if (!item || item->type() != cppbor::ARRAY) { + return nullptr; + } + auto vals = item->asArray(); + if (vals->size() != 4 || vals->get(0)->type() != cppbor::UINT || + vals->get(1)->type() != cppbor::TSTR || + vals->get(2)->type() != cppbor::UINT || + vals->get(3)->type() != cppbor::SIMPLE) { return {}; } - - char* raw_path; - std::size_t len; - err = cbor_value_dup_text_string(&val, &raw_path, &len, &val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - std::pmr::string path(raw_path, len); - delete raw_path; - - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - uint64_t hash = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - uint32_t play_count = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_is_boolean(&val)) { - return {}; - } - - bool is_tombstoned; - err = cbor_value_get_boolean(&val, &is_tombstoned); - if (err != CborNoError) { - return {}; - } - - return std::make_shared<TrackData>(id, path, hash, play_count, is_tombstoned); + TrackId id = vals->get(0)->asUint()->unsignedValue(); + auto path = vals->get(1)->asViewTstr()->view(); + uint64_t hash = vals->get(2)->asUint()->unsignedValue(); + bool tombstoned = vals->get(3)->asBool()->value(); + return std::make_shared<TrackData>( + id, std::pmr::string{path.data(), path.size()}, hash, tombstoned); } /* 'H/ 0xBEEF' */ -auto EncodeHashKey(const uint64_t& hash) -> OwningSlice { - ostringstream output; - output.put(kHashPrefix).put(kFieldSeparator); - - uint8_t buf[16]; - CborEncoder enc; - cbor_encoder_init(&enc, buf, sizeof(buf), 0); - cbor_encode_uint(&enc, hash); - std::size_t len = cbor_encoder_get_buffer_size(&enc, buf); - output.write(reinterpret_cast<char*>(buf), len); - - return OwningSlice(output.str()); +auto EncodeHashKey(const uint64_t& hash) -> std::string { + return std::string{kHashPrefix, kFieldSeparator} + + cppbor::Uint{hash}.toString(); } auto ParseHashValue(const leveldb::Slice& slice) -> std::optional<TrackId> { return BytesToTrackId({slice.data(), slice.size()}); } -auto EncodeHashValue(TrackId id) -> OwningSlice { +auto EncodeHashValue(TrackId id) -> std::string { return TrackIdToBytes(id); } /* 'I/' */ -auto EncodeAllIndexesPrefix() -> OwningSlice { - char data[2] = {kIndexPrefix, kFieldSeparator}; - return OwningSlice({data, 2}); -} - -auto AppendIndexHeader(const IndexKey::Header& header, ostringstream* out) - -> void { - *out << kIndexPrefix << kFieldSeparator; - - // Construct the header. - uint8_t* buf; - std::size_t buf_len = cbor_encode(&buf, [&](CborEncoder* enc) { - CborEncoder array_encoder; - CborError err; - err = cbor_encoder_create_array(enc, &array_encoder, 3); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_uint(&array_encoder, header.id); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_uint(&array_encoder, header.depth); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encode_uint(&array_encoder, header.components_hash); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - err = cbor_encoder_close_container(enc, &array_encoder); - if (err != CborNoError && err != CborErrorOutOfMemory) { - ESP_LOGE(kTag, "encoding err %u", err); - return; - } - }); - std::pmr::string encoded{reinterpret_cast<char*>(buf), buf_len}; - delete buf; - *out << encoded << kFieldSeparator; +auto EncodeAllIndexesPrefix() -> std::string { + return {kIndexPrefix, kFieldSeparator}; } -auto EncodeIndexPrefix(const IndexKey::Header& header) -> OwningSlice { - ostringstream out; - AppendIndexHeader(header, &out); - return OwningSlice(out.str()); +auto EncodeIndexPrefix(const IndexKey::Header& header) -> std::string { + std::ostringstream out; + out.put(kIndexPrefix).put(kFieldSeparator); + cppbor::Array val{ + cppbor::Uint{header.id}, + cppbor::Uint{header.depth}, + cppbor::Uint{header.components_hash}, + }; + out << val.toString() << kFieldSeparator; + return out.str(); } /* @@ -303,93 +137,51 @@ auto EncodeIndexPrefix(const IndexKey::Header& header) -> OwningSlice { * components. We store disambiguation information in the trailer; just a track * id for now, but could reasonably be something like 'release year' as well. */ -auto EncodeIndexKey(const IndexKey& key) -> OwningSlice { - ostringstream out; +auto EncodeIndexKey(const IndexKey& key) -> std::string { + std::ostringstream out{}; - // Construct the header. - AppendIndexHeader(key.header, &out); + out << EncodeIndexPrefix(key.header); // The component should already be UTF-8 encoded, so just write it. if (key.item) { - out << *key.item; + out << *key.item << kFieldSeparator; } - // Construct the footer. - out << kFieldSeparator; if (key.track) { - auto encoded = TrackIdToBytes(*key.track); - out << encoded.data; + out << TrackIdToBytes(*key.track); } - return OwningSlice(out.str()); + + return out.str(); } auto ParseIndexKey(const leveldb::Slice& slice) -> std::optional<IndexKey> { IndexKey result{}; auto prefix = EncodeAllIndexesPrefix(); - if (!slice.starts_with(prefix.slice)) { + if (!slice.starts_with(prefix)) { return {}; } - std::string key_data = slice.ToString().substr(prefix.data.size()); - std::size_t header_length = 0; - { - CborParser parser; - CborValue container; - CborError err; - err = cbor_parser_init(reinterpret_cast<const uint8_t*>(key_data.data()), - key_data.size(), 0, &parser, &container); - if (err != CborNoError || !cbor_value_is_container(&container)) { - return {}; - } - - CborValue val; - err = cbor_value_enter_container(&container, &val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - - uint64_t raw_int; - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - result.header.id = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - result.header.depth = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_is_unsigned_integer(&val)) { - return {}; - } - - err = cbor_value_get_uint64(&val, &raw_int); - if (err != CborNoError) { - return {}; - } - result.header.components_hash = raw_int; - err = cbor_value_advance(&val); - if (err != CborNoError || !cbor_value_at_end(&val)) { - return {}; - } - - const uint8_t* next_byte = cbor_value_get_next_byte(&val); - header_length = - next_byte - reinterpret_cast<const uint8_t*>(key_data.data()); + std::string key_data = slice.ToString().substr(prefix.size()); + auto [key, end_of_key, err] = cppbor::parseWithViews( + reinterpret_cast<const uint8_t*>(key_data.data()), key_data.size()); + if (!key || key->type() != cppbor::ARRAY) { + return {}; } - - if (header_length == 0) { + auto as_array = key->asArray(); + if (as_array->size() != 3 || as_array->get(0)->type() != cppbor::UINT || + as_array->get(1)->type() != cppbor::UINT || + as_array->get(2)->type() != cppbor::UINT) { return {}; } + result.header.id = as_array->get(0)->asUint()->unsignedValue(); + result.header.depth = as_array->get(1)->asUint()->unsignedValue(); + result.header.components_hash = as_array->get(2)->asUint()->unsignedValue(); + + size_t header_length = + reinterpret_cast<const char*>(end_of_key) - key_data.data(); - if (header_length >= key_data.size()) { + if (header_length == 0 || header_length >= key_data.size()) { return {}; } @@ -411,27 +203,17 @@ auto ParseIndexKey(const leveldb::Slice& slice) -> std::optional<IndexKey> { return result; } -auto TrackIdToBytes(TrackId id) -> OwningSlice { - uint8_t buf[8]; - CborEncoder enc; - cbor_encoder_init(&enc, buf, sizeof(buf), 0); - cbor_encode_uint(&enc, id); - std::size_t len = cbor_encoder_get_buffer_size(&enc, buf); - std::pmr::string as_str(reinterpret_cast<char*>(buf), len); - return OwningSlice(as_str); +auto TrackIdToBytes(TrackId id) -> std::string { + return cppbor::Uint{id}.toString(); } auto BytesToTrackId(cpp::span<const char> bytes) -> std::optional<TrackId> { - CborParser parser; - CborValue val; - cbor_parser_init(reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size(), - 0, &parser, &val); - if (!cbor_value_is_unsigned_integer(&val)) { + auto [res, unused, err] = cppbor::parse( + reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size()); + if (!res || res->type() != cppbor::UINT) { return {}; } - uint64_t raw_id; - cbor_value_get_uint64(&val, &raw_id); - return raw_id; + return res->asUint()->unsignedValue(); } } // namespace database diff --git a/src/database/track.cpp b/src/database/track.cpp index f48bb8ed..ca180124 100644 --- a/src/database/track.cpp +++ b/src/database/track.cpp @@ -53,15 +53,15 @@ auto TrackTags::Hash() const -> uint64_t { } auto TrackData::UpdateHash(uint64_t new_hash) const -> TrackData { - return TrackData(id_, filepath_, new_hash, play_count_, is_tombstoned_); + return TrackData(id_, filepath_, new_hash, is_tombstoned_); } auto TrackData::Entomb() const -> TrackData { - return TrackData(id_, filepath_, tags_hash_, play_count_, true); + return TrackData(id_, filepath_, tags_hash_, true); } auto TrackData::Exhume(const std::pmr::string& new_path) const -> TrackData { - return TrackData(id_, new_path, tags_hash_, play_count_, false); + return TrackData(id_, new_path, tags_hash_, false); } auto Track::TitleOrFilename() const -> std::pmr::string { |
