1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "flutter/fml/icu_util.h" |
6 | |
7 | #include <memory> |
8 | #include <mutex> |
9 | |
10 | #include "flutter/fml/build_config.h" |
11 | #include "flutter/fml/logging.h" |
12 | #include "flutter/fml/mapping.h" |
13 | #include "flutter/fml/native_library.h" |
14 | #include "flutter/fml/paths.h" |
15 | #include "third_party/icu/source/common/unicode/udata.h" |
16 | |
17 | namespace fml { |
18 | namespace icu { |
19 | |
20 | class ICUContext { |
21 | public: |
22 | explicit ICUContext(const std::string& icu_data_path) : valid_(false) { |
23 | valid_ = SetupMapping(icu_data_path) && SetupICU(); |
24 | } |
25 | |
26 | explicit ICUContext(std::unique_ptr<Mapping> mapping) |
27 | : mapping_(std::move(mapping)) { |
28 | valid_ = SetupICU(); |
29 | } |
30 | |
31 | ~ICUContext() = default; |
32 | |
33 | bool SetupMapping(const std::string& icu_data_path) { |
34 | // Check if the path exists and it readable directly. |
35 | auto fd = |
36 | fml::OpenFile(icu_data_path.c_str(), false, fml::FilePermission::kRead); |
37 | |
38 | // Check the path relative to the current executable. |
39 | if (!fd.is_valid()) { |
40 | auto directory = fml::paths::GetExecutableDirectoryPath(); |
41 | |
42 | if (!directory.first) { |
43 | return false; |
44 | } |
45 | |
46 | std::string path_relative_to_executable = |
47 | paths::JoinPaths({directory.second, icu_data_path}); |
48 | |
49 | fd = fml::OpenFile(path_relative_to_executable.c_str(), false, |
50 | fml::FilePermission::kRead); |
51 | } |
52 | |
53 | if (!fd.is_valid()) { |
54 | return false; |
55 | } |
56 | |
57 | std::initializer_list<FileMapping::Protection> protection = { |
58 | fml::FileMapping::Protection::kRead}; |
59 | |
60 | auto file_mapping = |
61 | std::make_unique<FileMapping>(fd, std::move(protection)); |
62 | |
63 | if (file_mapping->GetSize() != 0) { |
64 | mapping_ = std::move(file_mapping); |
65 | return true; |
66 | } |
67 | |
68 | return false; |
69 | } |
70 | |
71 | bool SetupICU() { |
72 | if (GetSize() == 0) { |
73 | return false; |
74 | } |
75 | |
76 | UErrorCode err_code = U_ZERO_ERROR; |
77 | udata_setCommonData(GetMapping(), &err_code); |
78 | return (err_code == U_ZERO_ERROR); |
79 | } |
80 | |
81 | const uint8_t* GetMapping() const { |
82 | return mapping_ ? mapping_->GetMapping() : nullptr; |
83 | } |
84 | |
85 | size_t GetSize() const { return mapping_ ? mapping_->GetSize() : 0; } |
86 | |
87 | bool IsValid() const { return valid_; } |
88 | |
89 | private: |
90 | bool valid_; |
91 | std::unique_ptr<Mapping> mapping_; |
92 | |
93 | FML_DISALLOW_COPY_AND_ASSIGN(ICUContext); |
94 | }; |
95 | |
96 | void InitializeICUOnce(const std::string& icu_data_path) { |
97 | static ICUContext* context = new ICUContext(icu_data_path); |
98 | FML_CHECK(context->IsValid()) |
99 | << "Must be able to initialize the ICU context. Tried: "<< icu_data_path; |
100 | } |
101 | |
102 | std::once_flag g_icu_init_flag; |
103 | void InitializeICU(const std::string& icu_data_path) { |
104 | std::call_once(g_icu_init_flag, |
105 | [&icu_data_path]() { InitializeICUOnce(icu_data_path); }); |
106 | } |
107 | |
108 | void InitializeICUFromMappingOnce(std::unique_ptr<Mapping> mapping) { |
109 | static ICUContext* context = new ICUContext(std::move(mapping)); |
110 | FML_CHECK(context->IsValid()) |
111 | << "Unable to initialize the ICU context from a mapping."; |
112 | } |
113 | |
114 | void InitializeICUFromMapping(std::unique_ptr<Mapping> mapping) { |
115 | std::call_once(g_icu_init_flag, [mapping = std::move(mapping)]() mutable { |
116 | InitializeICUFromMappingOnce(std::move(mapping)); |
117 | }); |
118 | } |
119 | |
120 | } // namespace icu |
121 | } // namespace fml |
122 |