• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "file_resource_manager.h"
17 
18 #include <iomanip>
19 #include <sstream>
20 
21 #include <base/util/base64_decode.h>
22 #include <base/util/base64_encode.h>
23 #include <core/io/intf_filesystem_api.h>
24 
25 #include <meta/interface/resource/intf_resource.h>
26 
27 #include "memfile.h"
28 
META_BEGIN_NAMESPACE()29 META_BEGIN_NAMESPACE()
30 
31 static bool ResourceMatches(
32     const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection, const CORE_NS::ResourceId& id)
33 {
34     for (auto&& s : selection) {
35         if ((s.hasWildCardName || id.name == s.name) && (s.hasWildCardGroup || id.group == s.group)) {
36             return true;
37         }
38     }
39     return false;
40 }
41 
FileResourceManager()42 FileResourceManager::FileResourceManager()
43 {
44     if (auto factory = CORE_NS::GetInstance<CORE_NS::IFileSystemApi>(CORE_NS::UID_FILESYSTEM_API_FACTORY)) {
45         fileManager_ = factory->CreateFilemanager();
46         fileManager_->RegisterFilesystem("file", factory->CreateStdFileSystem());
47     }
48 }
49 
AddListener(const IResourceListener::Ptr & p)50 void FileResourceManager::AddListener(const IResourceListener::Ptr& p)
51 {
52     std::unique_lock lock { listenerMutex_ };
53     for (auto&& v : listeners_) {
54         if (v.lock() == p) {
55             return;
56         }
57     }
58     listeners_.push_back(p);
59 }
RemoveListener(const IResourceListener::Ptr & p)60 void FileResourceManager::RemoveListener(const IResourceListener::Ptr& p)
61 {
62     std::unique_lock lock { listenerMutex_ };
63     auto it = listeners_.begin();
64     for (; it != listeners_.end() && it->lock() != p; ++it) {
65     }
66     if (it != listeners_.end()) {
67         listeners_.erase(it);
68     }
69 }
Notify(const BASE_NS::vector<CORE_NS::ResourceId> & ids,IResourceListener::EventType type)70 void FileResourceManager::Notify(const BASE_NS::vector<CORE_NS::ResourceId>& ids, IResourceListener::EventType type)
71 {
72     if (!ids.empty()) {
73         BASE_NS::vector<IResourceListener::WeakPtr> list;
74         {
75             std::shared_lock lock { listenerMutex_ };
76             list = listeners_;
77         }
78         for (auto&& v : list) {
79             if (auto l = v.lock()) {
80                 l->OnResourceEvent(type, ids);
81             }
82         }
83     }
84 }
AddResourceType(CORE_NS::IResourceType::Ptr p)85 bool FileResourceManager::AddResourceType(CORE_NS::IResourceType::Ptr p)
86 {
87     if (p) {
88         std::unique_lock lock { mutex_ };
89         types_[p->GetResourceType()] = p;
90     }
91     return p != nullptr;
92 }
RemoveResourceType(const CORE_NS::ResourceType & type)93 bool FileResourceManager::RemoveResourceType(const CORE_NS::ResourceType& type)
94 {
95     std::unique_lock lock { mutex_ };
96     types_.erase(type);
97     return true;
98 }
GetResourceTypes() const99 BASE_NS::vector<CORE_NS::IResourceType::Ptr> FileResourceManager::GetResourceTypes() const
100 {
101     std::shared_lock lock { mutex_ };
102     BASE_NS::vector<CORE_NS::IResourceType::Ptr> res;
103     for (auto&& v : types_) {
104         res.push_back(v.second);
105     }
106     return res;
107 }
ReadHeader(const std::string & line)108 CORE_NS::ResourceId FileResourceManager::ReadHeader(const std::string& line)
109 {
110     std::istringstream in(line);
111     std::string name;
112     std::string group;
113     std::string path;
114     std::string type;
115     in >> std::quoted(name) >> std::quoted(group) >> std::quoted(path) >> type;
116     if (!in || type.size() != 36U) {
117         return {};
118     }
119 
120     auto data = BASE_NS::make_shared<ResourceData>();
121     data->id =
122         CORE_NS::ResourceId { BASE_NS::string(name.data(), name.size()), BASE_NS::string(group.data(), group.size()) };
123     data->path = BASE_NS::string(path.data(), path.size());
124     data->type = BASE_NS::StringToUid(BASE_NS::string_view(type.data(), type.size()));
125 
126     std::string opt;
127     if (in >> opt) {
128         data->optionData = BASE_NS::Base64Decode(opt.c_str());
129     }
130     resources_[data->id.group][data->id.name] = data;
131     return data->id;
132 }
ReadHeaders(CORE_NS::IFile & file,BASE_NS::vector<CORE_NS::ResourceId> & result)133 bool FileResourceManager::ReadHeaders(CORE_NS::IFile& file, BASE_NS::vector<CORE_NS::ResourceId>& result)
134 {
135     std::string vec;
136     vec.resize(file.GetLength());
137     file.Read(vec.data(), vec.size());
138     std::istringstream ss(vec);
139     std::string line;
140     while (std::getline(ss, line)) {
141         if (!line.empty() && line[0] != '#') {
142             auto res = ReadHeader(line);
143             if (!res.IsValid()) {
144                 return false;
145             }
146             result.push_back(std::move(res));
147         }
148     }
149     return true;
150 }
Import(BASE_NS::string_view url)151 FileResourceManager::Result FileResourceManager::Import(BASE_NS::string_view url)
152 {
153     BASE_NS::vector<CORE_NS::ResourceId> result;
154     {
155         std::unique_lock lock { mutex_ };
156         auto f = fileManager_->OpenFile(url);
157         if (!f) {
158             CORE_LOG_W("Failed to open resource manager file: %s", BASE_NS::string(url).c_str());
159             return Result::FILE_NOT_FOUND;
160         }
161         if (!ReadHeaders(*f, result)) {
162             return Result::INVALID_FILE;
163         }
164     }
165     Notify(result, IResourceListener::EventType::ADDED);
166     return Result::OK;
167 }
GetResourceInfos(const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection) const168 BASE_NS::vector<CORE_NS::ResourceInfo> FileResourceManager::GetResourceInfos(
169     const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection) const
170 {
171     std::shared_lock lock { mutex_ };
172     BASE_NS::vector<CORE_NS::ResourceInfo> infos;
173     for (auto&& g : resources_) {
174         for (auto&& v : g.second) {
175             if (ResourceMatches(selection, v.second->id)) {
176                 infos.push_back(*v.second);
177             }
178         }
179     }
180     return infos;
181 }
GetResourceGroups() const182 BASE_NS::vector<BASE_NS::string> FileResourceManager::GetResourceGroups() const
183 {
184     std::shared_lock lock { mutex_ };
185     BASE_NS::vector<BASE_NS::string> groups;
186     for (auto&& g : resources_) {
187         groups.push_back(g.first);
188     }
189     return groups;
190 }
GetResourceInfo(const CORE_NS::ResourceId & id) const191 CORE_NS::ResourceInfo FileResourceManager::GetResourceInfo(const CORE_NS::ResourceId& id) const
192 {
193     std::shared_lock lock { mutex_ };
194     auto git = resources_.find(id.group);
195     if (git != resources_.end()) {
196         auto it = git->second.find(id.name);
197         return it != git->second.end() ? CORE_NS::ResourceInfo { *it->second } : CORE_NS::ResourceInfo {};
198     }
199     return {};
200 }
FindResource(const CORE_NS::ResourceId & id)201 BASE_NS::shared_ptr<ResourceData> FileResourceManager::FindResource(const CORE_NS::ResourceId& id)
202 {
203     auto git = resources_.find(id.group);
204     if (git == resources_.end()) {
205         return nullptr;
206     }
207     auto it = git->second.find(id.name);
208     if (it == git->second.end()) {
209         return nullptr;
210     }
211     return it->second;
212 }
GetResource(const CORE_NS::ResourceId & id,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)213 CORE_NS::IResource::Ptr FileResourceManager::GetResource(
214     const CORE_NS::ResourceId& id, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
215 {
216     {
217         std::shared_lock lock { mutex_ };
218         if (auto res = FindResource(id)) {
219             if (res->object) {
220                 return res->object;
221             }
222         }
223     }
224     return ConstructResource(id, context);
225 }
GetResources(const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)226 BASE_NS::vector<CORE_NS::IResource::Ptr> FileResourceManager::GetResources(
227     const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection,
228     const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
229 {
230     BASE_NS::vector<CORE_NS::IResource::Ptr> res;
231     BASE_NS::vector<CORE_NS::ResourceId> populate;
232     {
233         std::shared_lock lock { mutex_ };
234         for (auto&& g : resources_) {
235             for (auto&& v : g.second) {
236                 auto& r = *v.second;
237                 if (ResourceMatches(selection, r.id)) {
238                     if (r.object) {
239                         res.push_back(r.object);
240                     } else {
241                         populate.push_back(r.id);
242                     }
243                 }
244             }
245         }
246     }
247     for (auto&& v : populate) {
248         if (auto p = ConstructResource(v, context)) {
249             res.push_back(p);
250         }
251     }
252     return res;
253 }
SaveResourceHeader(const ResourceData & r,CORE_NS::IFile & file)254 static void SaveResourceHeader(const ResourceData& r, CORE_NS::IFile& file)
255 {
256     std::ostringstream out;
257     out << std::quoted(r.id.name.c_str()) << " " << std::quoted(r.id.group.c_str()) << " "
258         << std::quoted(r.path.c_str()) << " " << BASE_NS::to_string(r.type).c_str() << " ";
259     out << BASE_NS::Base64Encode(r.optionData).c_str();
260     out << "\n";
261     auto buf = out.str();
262     file.Write(buf.data(), buf.size());
263 }
SaveResourceData(const ResourceData & r)264 FileResourceManager::Result FileResourceManager::SaveResourceData(const ResourceData& r)
265 {
266     if (!r.object) {
267         return Result::NO_RESOURCE_DATA;
268     }
269     auto it = types_.find(r.type);
270     if (it == types_.end()) {
271         CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(r.type).c_str());
272         return Result::NO_RESOURCE_TYPE;
273     }
274     if (!r.path.empty()) {
275         MemFile data;
276         if (!it->second->SaveResource(r.object, CORE_NS::IResourceType::StorageInfo{nullptr, &data, r.id, r.path,
277                                                                                     GetSelf<IResourceManager>()})) {
278             CORE_LOG_W("Failed to save resource: %s", r.id.ToString().c_str());
279             return Result::EXPORT_FAILURE;
280         }
281         if (data.GetLength()) {
282             auto f = fileManager_->CreateFile(r.path);
283             if (!f) {
284                 CORE_LOG_W("Failed to open resource file: %s", r.path.c_str());
285                 return Result::FILE_WRITE_ERROR;
286             }
287             f->Write(data.RawData(), data.GetLength());
288         }
289     }
290     return Result::OK;
291 }
UpdateOptions(ResourceData & r)292 bool FileResourceManager::UpdateOptions(ResourceData& r)
293 {
294     if (r.object) {
295         auto it = types_.find(r.type);
296         if (it == types_.end()) {
297             CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(r.type).c_str());
298             return false;
299         }
300         MemFile opts;
301         if (!it->second->SaveResource(r.object, CORE_NS::IResourceType::StorageInfo{&opts, nullptr, r.id, r.path,
302                                                                                     GetSelf<IResourceManager>()})) {
303             CORE_LOG_W("Failed to save resource options");
304             return false;
305         }
306         r.optionData = opts.Data();
307     }
308     return true;
309 }
Export(BASE_NS::string_view filePath,const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection)310 FileResourceManager::Result FileResourceManager::Export(
311     BASE_NS::string_view filePath, const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection)
312 {
313     std::unique_lock lock { mutex_ };
314     auto f = fileManager_->CreateFile(filePath);
315     if (!f) {
316         CORE_LOG_W("Failed to create resource manager file: %s", BASE_NS::string(filePath).c_str());
317         return Result::FILE_WRITE_ERROR;
318     }
319     BASE_NS::string vstr = "# Lume Resource Index Version 1\n";
320     f->Write(vstr.data(), vstr.size());
321     for (auto&& g : resources_) {
322         for (auto&& v : g.second) {
323             auto& res = *v.second;
324             if (ResourceMatches(selection, res.id)) {
325                 UpdateOptions(res);
326                 SaveResourceHeader(res, *f);
327                 SaveResourceData(res);
328             }
329         }
330     }
331     return Result::OK;
332 }
333 
AddResource(const CORE_NS::IResource::Ptr & resource)334 bool FileResourceManager::AddResource(const CORE_NS::IResource::Ptr& resource)
335 {
336     return AddResource(resource, resource->GetResourceId().name);
337 }
AddResource(const CORE_NS::IResource::Ptr & resource,BASE_NS::string_view path)338 bool FileResourceManager::AddResource(const CORE_NS::IResource::Ptr& resource, BASE_NS::string_view path)
339 {
340     {
341         std::unique_lock lock { mutex_ };
342         auto it = types_.find(resource->GetResourceType());
343         if (it == types_.end()) {
344             CORE_LOG_W(
345                 "Resource type not registered [type=%s]", BASE_NS::to_string(resource->GetResourceType()).c_str());
346             return false;
347         }
348         auto id = resource->GetResourceId();
349         ResourceData d;
350         d.object = resource;
351         d.type = resource->GetResourceType();
352         d.id = id;
353         d.path = path;
354         MemFile opts;
355         if (!it->second->SaveResource(resource, CORE_NS::IResourceType::StorageInfo{&opts, nullptr, d.id, d.path,
356                                                                                     GetSelf<IResourceManager>()})) {
357             CORE_LOG_W("Failed to save resource options");
358             return false;
359         }
360         d.optionData = opts.Data();
361         resources_[id.group][id.name] = BASE_NS::make_shared<ResourceData>(BASE_NS::move(d));
362     }
363     Notify({ resource->GetResourceId() }, IResourceListener::EventType::ADDED);
364     return true;
365 }
AddResource(const CORE_NS::ResourceId & id,const CORE_NS::ResourceType & type,BASE_NS::string_view path,const CORE_NS::IResourceOptions::ConstPtr & options)366 bool FileResourceManager::AddResource(const CORE_NS::ResourceId& id, const CORE_NS::ResourceType& type,
367     BASE_NS::string_view path, const CORE_NS::IResourceOptions::ConstPtr& options)
368 {
369     {
370         std::unique_lock lock { mutex_ };
371         auto it = types_.find(type);
372         if (it == types_.end()) {
373             CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(type).c_str());
374             return false;
375         }
376         ResourceData d;
377         d.type = type;
378         d.id = id;
379         d.path = path;
380         if (options) {
381             MemFile opts;
382             options->Save(opts, GetSelf<IResourceManager>());
383             d.optionData = opts.Data();
384         }
385         resources_[id.group][id.name] = BASE_NS::make_shared<ResourceData>(BASE_NS::move(d));
386     }
387     Notify({ id }, IResourceListener::EventType::ADDED);
388     return true;
389 }
ReloadResource(const CORE_NS::IResource::Ptr & resource,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)390 bool FileResourceManager::ReloadResource(
391     const CORE_NS::IResource::Ptr& resource, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
392 {
393     CORE_NS::IFile::Ptr f;
394     MemFile opts;
395     BASE_NS::string path;
396     CORE_NS::IResourceType::Ptr type;
397     {
398         std::unique_lock lock { mutex_ };
399         auto it = types_.find(resource->GetResourceType());
400         if (it == types_.end()) {
401             CORE_LOG_W(
402                 "Resource type not registered [type=%s]", BASE_NS::to_string(resource->GetResourceType()).c_str());
403             return false;
404         }
405         type = it->second;
406         auto res = FindResource(resource->GetResourceId());
407         if (!res) {
408             CORE_LOG_W("No such resource: %s", resource->GetResourceId().ToString().c_str());
409             return false;
410         }
411         path = res->path;
412         if (!path.empty()) {
413             f = fileManager_->OpenFile(path);
414             if (!f) {
415                 CORE_LOG_W("Failed to open resource file: %s", path.c_str());
416                 return false;
417             }
418         }
419         opts = MemFile(res->optionData);
420     }
421     CORE_NS::IResourceType::StorageInfo info { &opts, f.get(), resource->GetResourceId(), path,
422         GetSelf<IResourceManager>(), context };
423     return type->ReloadResource(info, resource);
424 }
RenameResource(const CORE_NS::ResourceId & id,const CORE_NS::ResourceId & newId)425 bool FileResourceManager::RenameResource(const CORE_NS::ResourceId& id, const CORE_NS::ResourceId& newId)
426 {
427     {
428         std::unique_lock lock { mutex_ };
429         auto git = resources_.find(id.group);
430         if (git == resources_.end()) {
431             return false;
432         }
433         auto it = git->second.find(id.name);
434         if (it == git->second.end()) {
435             return false;
436         }
437         auto& v = resources_[newId.group][newId.name];
438         if (v) {
439             return false;
440         }
441         v = it->second;
442         git->second.erase(it);
443     }
444 
445     Notify({ id }, IResourceListener::EventType::REMOVED);
446     Notify({ newId }, IResourceListener::EventType::ADDED);
447     return true;
448 }
PurgeResource(const CORE_NS::ResourceId & id)449 bool FileResourceManager::PurgeResource(const CORE_NS::ResourceId& id)
450 {
451     std::unique_lock lock { mutex_ };
452     if (auto res = FindResource(id)) {
453         res->object = nullptr;
454         return true;
455     }
456     CORE_LOG_W("No such resource: %s", id.ToString().c_str());
457     return false;
458 }
RemoveResource(const CORE_NS::ResourceId & id)459 bool FileResourceManager::RemoveResource(const CORE_NS::ResourceId& id)
460 {
461     bool res = false;
462     {
463         std::unique_lock lock { mutex_ };
464         auto git = resources_.find(id.group);
465         res = git != resources_.end() && git->second.erase(id.name) == 1;
466     }
467     if (res) {
468         Notify({ id }, IResourceListener::EventType::REMOVED);
469     }
470     return res;
471 }
PurgeGroup(BASE_NS::string_view group)472 size_t FileResourceManager::PurgeGroup(BASE_NS::string_view group)
473 {
474     size_t count = 0;
475     std::unique_lock lock { mutex_ };
476     auto git = resources_.find(group);
477     if (git != resources_.end()) {
478         for (auto it = git->second.begin(); it != git->second.end(); ++it) {
479             if (it->second->object) {
480                 it->second->object = nullptr;
481                 ++count;
482             }
483         }
484     }
485     return count;
486 }
RemoveGroup(BASE_NS::string_view group)487 bool FileResourceManager::RemoveGroup(BASE_NS::string_view group)
488 {
489     bool res = false;
490     BASE_NS::vector<CORE_NS::ResourceId> result;
491     {
492         std::unique_lock lock { mutex_ };
493         auto it = resources_.find(group);
494         res = it != resources_.end();
495         if (res) {
496             for (auto&& g : it->second) {
497                 result.push_back(g.second->id);
498             }
499             resources_.erase(it);
500         }
501     }
502     if (res) {
503         Notify(result, IResourceListener::EventType::REMOVED);
504     }
505     return res;
506 }
RemoveAllResources()507 void FileResourceManager::RemoveAllResources()
508 {
509     BASE_NS::vector<CORE_NS::ResourceId> result;
510     {
511         std::unique_lock lock { mutex_ };
512         for (auto&& g : resources_) {
513             for (auto&& v : g.second) {
514                 result.push_back(v.second->id);
515             }
516         }
517         resources_.clear();
518     }
519     Notify(result, IResourceListener::EventType::REMOVED);
520 }
521 
ExportResourcePayload(const CORE_NS::IResource::Ptr & resource)522 FileResourceManager::Result FileResourceManager::ExportResourcePayload(const CORE_NS::IResource::Ptr& resource)
523 {
524     std::shared_lock lock { mutex_ };
525     auto res = FindResource(resource->GetResourceId());
526     if (!res) {
527         CORE_LOG_W("No such resource: %s", resource->GetResourceId().ToString().c_str());
528         return Result::NO_RESOURCE_DATA;
529     }
530     return SaveResourceData(*res);
531 }
SetFileManager(CORE_NS::IFileManager::Ptr fileManager)532 void FileResourceManager::SetFileManager(CORE_NS::IFileManager::Ptr fileManager)
533 {
534     std::unique_lock lock { mutex_ };
535     fileManager_ = BASE_NS::move(fileManager);
536 }
ConstructResource(const CORE_NS::ResourceId & id,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)537 CORE_NS::IResource::Ptr FileResourceManager::ConstructResource(
538     const CORE_NS::ResourceId& id, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
539 {
540     BASE_NS::shared_ptr<ResourceData> res;
541     CORE_NS::IResourceType::ConstPtr type;
542     BASE_NS::string path;
543     BASE_NS::vector<uint8_t> optionData;
544     CORE_NS::IFileManager::Ptr fileManager;
545 
546     {
547         std::shared_lock lock { mutex_ };
548         res = FindResource(id);
549         if (res) {
550             auto tit = types_.find(res->type);
551             if (tit != types_.end()) {
552                 type = tit->second;
553                 path = res->path;
554                 optionData = res->optionData;
555                 fileManager = fileManager_;
556             }
557         }
558     }
559 
560     CORE_NS::IResource::Ptr resObj;
561     if (res && type) {
562         CORE_NS::IFile::Ptr f;
563         if (!path.empty()) {
564             f = fileManager->OpenFile(path);
565             if (!f) {
566                 CORE_LOG_W("Failed to open resource file: %s", path.c_str());
567                 return nullptr;
568             }
569         }
570         MemFile opts(optionData);
571         resObj = type->LoadResource(
572             CORE_NS::IResourceType::StorageInfo { &opts, f.get(), id, path, GetSelf<IResourceManager>(), context });
573         if (resObj) {
574             if (auto i = interface_cast<CORE_NS::ISetResourceId>(resObj)) {
575                 i->SetResourceId(id);
576             }
577         } else {
578             CORE_LOG_W("Failed to load resource: %s", id.ToString().c_str());
579         }
580     }
581 
582     if (resObj) {
583         std::unique_lock lock { mutex_ };
584         if (res->object) {
585             resObj = res->object;
586         } else {
587             res->object = resObj;
588         }
589     }
590     return resObj;
591 }
592 
593 META_END_NAMESPACE()
594