pico_phone 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ // First-party pico_phone file. See carrier_mapper.h.
2
+
3
+ #include "carrier_mapper.h"
4
+
5
+ #include <algorithm>
6
+ #include <cstring>
7
+ #include <string>
8
+
9
+ #include "phonenumbers/geocoding/area_code_map.h"
10
+ #include "phonenumbers/geocoding/carrier_data.h"
11
+ #include "phonenumbers/geocoding/mapping_file_provider.h"
12
+ #include "phonenumbers/phonenumber.pb.h"
13
+
14
+ #include "absl/synchronization/mutex.h"
15
+
16
+ namespace i18n {
17
+ namespace phonenumbers {
18
+
19
+ using std::string;
20
+
21
+ namespace {
22
+
23
+ // Returns true if s1 comes strictly before s2 in lexicographic order.
24
+ bool IsLowerThan(const char* s1, const char* s2) {
25
+ return strcmp(s1, s2) < 0;
26
+ }
27
+
28
+ } // namespace
29
+
30
+ PhoneNumberCarrierMapper::PhoneNumberCarrierMapper() {
31
+ provider_.reset(new MappingFileProvider(get_carrier_country_calling_codes(),
32
+ get_carrier_country_calling_codes_size(),
33
+ get_carrier_country_languages));
34
+ prefix_language_code_pairs_ = get_carrier_prefix_language_code_pairs();
35
+ prefix_language_code_pairs_size_ = get_carrier_prefix_language_code_pairs_size();
36
+ get_prefix_descriptions_ = get_carrier_prefix_descriptions;
37
+ }
38
+
39
+ PhoneNumberCarrierMapper::~PhoneNumberCarrierMapper() {
40
+ // Pointer-taking constructor, not the reference-taking one: this file
41
+ // compiles against two different Abseil versions depending on build path
42
+ // (vendored 20260526.0 for the static/native build, whatever Debian/Ubuntu
43
+ // packages for the dynamic/apt-linked build), and only the older
44
+ // pointer-taking overload exists in both. The vendored geocoder itself hit
45
+ // the same tension in the other direction (see build_deps.sh's
46
+ // absl::MutexLock patch) -- there it's a -Werror'd deprecation inside
47
+ // libphonenumber's own CMake build; here it's just a harmless warning
48
+ // since pico_phone's own extension build doesn't set -Werror.
49
+ absl::MutexLock l(&mu_);
50
+ for (AreaCodeMaps::const_iterator it = available_maps_.begin();
51
+ it != available_maps_.end(); ++it) {
52
+ delete it->second;
53
+ }
54
+ }
55
+
56
+ const AreaCodeMap* PhoneNumberCarrierMapper::GetPhonePrefixDescriptions(
57
+ int prefix, const string& language, const string& script,
58
+ const string& region) const {
59
+ string filename;
60
+ provider_->GetFileName(prefix, language, script, region, &filename);
61
+ if (filename.empty()) {
62
+ return NULL;
63
+ }
64
+ AreaCodeMaps::const_iterator it = available_maps_.find(filename);
65
+ if (it == available_maps_.end()) {
66
+ return LoadAreaCodeMapFromFile(filename);
67
+ }
68
+ return it->second;
69
+ }
70
+
71
+ const AreaCodeMap* PhoneNumberCarrierMapper::LoadAreaCodeMapFromFile(
72
+ const string& filename) const {
73
+ const char** const prefix_language_code_pairs_end =
74
+ prefix_language_code_pairs_ + prefix_language_code_pairs_size_;
75
+ const char** const prefix_language_code_pair =
76
+ std::lower_bound(prefix_language_code_pairs_,
77
+ prefix_language_code_pairs_end,
78
+ filename.c_str(), IsLowerThan);
79
+ if (prefix_language_code_pair != prefix_language_code_pairs_end &&
80
+ filename.compare(*prefix_language_code_pair) == 0) {
81
+ AreaCodeMap* const m = new AreaCodeMap();
82
+ m->ReadAreaCodeMap(get_prefix_descriptions_(
83
+ prefix_language_code_pair - prefix_language_code_pairs_));
84
+ return available_maps_.insert(AreaCodeMaps::value_type(filename, m))
85
+ .first->second;
86
+ }
87
+ return NULL;
88
+ }
89
+
90
+ const char* PhoneNumberCarrierMapper::GetAreaDescription(
91
+ const PhoneNumber& number, const string& lang, const string& script,
92
+ const string& region) const {
93
+ const int country_calling_code = number.country_code();
94
+ // NANPA area is not split in C++ code (matches PhoneNumberOfflineGeocoder).
95
+ const int phone_prefix = country_calling_code;
96
+ absl::MutexLock l(&mu_);
97
+ const AreaCodeMap* const descriptions = GetPhonePrefixDescriptions(
98
+ phone_prefix, lang, script, region);
99
+ const char* description = descriptions ? descriptions->Lookup(number) : NULL;
100
+ // When a carrier name is not available in the requested language, fall
101
+ // back to English.
102
+ if ((!description || *description == '\0') && MayFallBackToEnglish(lang)) {
103
+ const AreaCodeMap* default_descriptions = GetPhonePrefixDescriptions(
104
+ phone_prefix, "en", "", "");
105
+ if (!default_descriptions) {
106
+ return "";
107
+ }
108
+ description = default_descriptions->Lookup(number);
109
+ }
110
+ return description ? description : "";
111
+ }
112
+
113
+ // Don't fall back to English if the requested language is among the following:
114
+ // - Chinese
115
+ // - Japanese
116
+ // - Korean
117
+ bool PhoneNumberCarrierMapper::MayFallBackToEnglish(const string& lang) const {
118
+ return lang.compare("zh") && lang.compare("ja") && lang.compare("ko");
119
+ }
120
+
121
+ string PhoneNumberCarrierMapper::GetNameForNumber(
122
+ const PhoneNumber& number, const string& lang) const {
123
+ return GetAreaDescription(number, lang, "", "");
124
+ }
125
+
126
+ } // namespace phonenumbers
127
+ } // namespace i18n
@@ -0,0 +1,88 @@
1
+ // First-party pico_phone file. A trimmed mirror of libphonenumber's
2
+ // PhoneNumberOfflineGeocoder (phonenumbers/geocoding/
3
+ // phonenumber_offline_geocoder.h in the vendored source), reusing the same
4
+ // AreaCodeMap/MappingFileProvider machinery -- lazy per (country calling
5
+ // code, language) file loading, cached for the life of the process -- but
6
+ // backed by carrier_data.cc instead of geocoding_data.cc, and with the
7
+ // ICU-based country-name fallback removed: carrier data has no natural
8
+ // "fall back to the country name" concept (unlike geocoding), so an
9
+ // unmapped prefix returns an empty string, matching phonelib's carrier
10
+ // semantics. This also means, unlike the geocoder, this class has no ICU
11
+ // dependency at all.
12
+
13
+ #ifndef PICO_PHONE_CARRIER_MAPPER_H_
14
+ #define PICO_PHONE_CARRIER_MAPPER_H_
15
+
16
+ #include <map>
17
+ #include <string>
18
+
19
+ #include "absl/synchronization/mutex.h"
20
+ #include "phonenumbers/base/memory/scoped_ptr.h"
21
+
22
+ namespace i18n {
23
+ namespace phonenumbers {
24
+
25
+ class AreaCodeMap;
26
+ class MappingFileProvider;
27
+ class PhoneNumber;
28
+ struct CountryLanguages;
29
+ struct PrefixDescriptions;
30
+
31
+ class PhoneNumberCarrierMapper {
32
+ private:
33
+ typedef std::map<std::string, const AreaCodeMap*> AreaCodeMaps;
34
+
35
+ public:
36
+ PhoneNumberCarrierMapper();
37
+
38
+ PhoneNumberCarrierMapper(const PhoneNumberCarrierMapper&) = delete;
39
+ PhoneNumberCarrierMapper& operator=(const PhoneNumberCarrierMapper&) = delete;
40
+
41
+ ~PhoneNumberCarrierMapper();
42
+
43
+ // Returns the carrier name for the given phone number in the given
44
+ // language, or an empty string if no carrier mapping is available for
45
+ // this prefix. lang is a two or three-letter lowercase ISO 639 language
46
+ // code, as with PhoneNumberOfflineGeocoder.
47
+ //
48
+ // The carrier name is the one the number was originally allocated to,
49
+ // however if the country supports mobile number portability the number
50
+ // might not belong to the returned carrier anymore (same caveat as
51
+ // upstream's Java-only PhoneNumberToCarrierMapper.getNameForNumber,
52
+ // verbatim -- see java/carrier/src/.../PhoneNumberToCarrierMapper.java
53
+ // in the vendored source tree).
54
+ std::string GetNameForNumber(const PhoneNumber& number,
55
+ const std::string& lang) const;
56
+
57
+ private:
58
+ typedef const PrefixDescriptions* (*prefix_descriptions_getter)(int index);
59
+
60
+ const AreaCodeMap* LoadAreaCodeMapFromFile(
61
+ const std::string& filename) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
62
+
63
+ const AreaCodeMap* GetPhonePrefixDescriptions(
64
+ int prefix, const std::string& language, const std::string& script,
65
+ const std::string& region) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
66
+
67
+ const char* GetAreaDescription(const PhoneNumber& number,
68
+ const std::string& lang,
69
+ const std::string& script,
70
+ const std::string& region) const
71
+ ABSL_LOCKS_EXCLUDED(mu_);
72
+
73
+ bool MayFallBackToEnglish(const std::string& lang) const;
74
+
75
+ scoped_ptr<const MappingFileProvider> provider_;
76
+
77
+ const char** prefix_language_code_pairs_;
78
+ int prefix_language_code_pairs_size_;
79
+ prefix_descriptions_getter get_prefix_descriptions_;
80
+
81
+ mutable absl::Mutex mu_;
82
+ mutable AreaCodeMaps available_maps_ ABSL_GUARDED_BY(mu_);
83
+ };
84
+
85
+ } // namespace phonenumbers
86
+ } // namespace i18n
87
+
88
+ #endif // PICO_PHONE_CARRIER_MAPPER_H_
@@ -2,6 +2,14 @@ require "mkmf-rice"
2
2
 
3
3
  $CXXFLAGS << ' -std=c++17'
4
4
 
5
+ # carrier_mapper.cc needs AreaCodeMap/MappingFileProvider/the
6
+ # CountryLanguages+PrefixDescriptions structs, none of which libphonenumber
7
+ # installs publicly (only phonenumber_offline_geocoder.h is installed) in
8
+ # either build path below. Headers vendored verbatim under
9
+ # vendor_headers/phonenumbers/geocoding/ -- see that directory's own
10
+ # comments. Needed unconditionally, regardless of NATIVE_BUILD.
11
+ $INCFLAGS << " -I#{File.expand_path("vendor_headers", __dir__)}"
12
+
5
13
  VENDOR_INSTALL = File.expand_path("vendor/install", __dir__)
6
14
  NATIVE_BUILD = ENV["PICO_PHONE_NATIVE_BUILD"] == "1"
7
15
 
@@ -3,6 +3,7 @@
3
3
  #include <string.h>
4
4
  #include <list>
5
5
  #include <set>
6
+ #include <vector>
6
7
  #include <phonenumbers/phonenumber.pb.h>
7
8
  #include <phonenumbers/phonenumberutil.h>
8
9
  #include <phonenumbers/shortnumberinfo.h>
@@ -13,6 +14,8 @@
13
14
  // any ICU header is reachable.
14
15
  #undef UChar
15
16
  #include <phonenumbers/geocoding/phonenumber_offline_geocoder.h>
17
+ #include "carrier_mapper.h"
18
+ #include "timezone_mapper.h"
16
19
 
17
20
  using namespace Rice;
18
21
  using namespace i18n::phonenumbers;
@@ -223,6 +226,51 @@ VALUE parsed_number_geo_name(int argc, VALUE *argv, VALUE self) {
223
226
  return rb_str_new(description.c_str(), description.size());
224
227
  }
225
228
 
229
+ // PhoneNumberCarrierMapper mirrors PhoneNumberOfflineGeocoder's lazy
230
+ // per-(prefix, language) file loading and caching -- see carrier_mapper.h.
231
+ static const PhoneNumberCarrierMapper& GetCarrierMapper() {
232
+ static PhoneNumberCarrierMapper instance;
233
+ return instance;
234
+ }
235
+
236
+ VALUE parsed_number_carrier_name(int argc, VALUE *argv, VALUE self) {
237
+ VALUE language;
238
+ rb_scan_args(argc, argv, "01", &language);
239
+
240
+ std::string language_code = RB_NIL_P(language)
241
+ ? "en"
242
+ : std::string(StringValuePtr(language), RSTRING_LEN(language));
243
+
244
+ PhoneNumber *phone_number;
245
+ TypedData_Get_Struct(self, PhoneNumber, &phone_number_type, phone_number);
246
+
247
+ std::string name = GetCarrierMapper().GetNameForNumber(*phone_number, language_code);
248
+
249
+ return rb_str_new(name.c_str(), name.size());
250
+ }
251
+
252
+ // PhoneNumberTimeZonesMapper eagerly loads its single flat prefix table
253
+ // once at construction (unlike the geocoder/carrier mappers, there's no
254
+ // per-language dimension or lazy per-file loading to do here) -- see
255
+ // timezone_mapper.h.
256
+ static const PhoneNumberTimeZonesMapper& GetTimeZonesMapper() {
257
+ static PhoneNumberTimeZonesMapper instance;
258
+ return instance;
259
+ }
260
+
261
+ Array parsed_number_timezones(Object self) {
262
+ PhoneNumber *phone_number;
263
+ TypedData_Get_Struct(self, PhoneNumber, &phone_number_type, phone_number);
264
+
265
+ std::vector<std::string> zones = GetTimeZonesMapper().GetTimeZonesForNumber(*phone_number);
266
+
267
+ Array result;
268
+ for (const auto& zone : zones) {
269
+ result.push(Object(rb_str_new(zone.c_str(), zone.size())));
270
+ }
271
+ return result;
272
+ }
273
+
226
274
  Object pico_phone_is_emergency_number(Object self, String number, String region) {
227
275
  return GetShortNumberInfo().IsEmergencyNumber(number.c_str(), region.c_str()) ? Qtrue : Qfalse;
228
276
  }
@@ -860,9 +908,11 @@ void Init_pico_phone() {
860
908
  .define_method("possible_with_reason", &parsed_number_possible_with_reason)
861
909
  .define_method("geographical?", &parsed_number_geographical)
862
910
  .define_method("can_be_internationally_dialled?", &parsed_number_can_be_internationally_dialled)
863
- .define_method("possible_for_type?", &parsed_number_possible_for_type);
911
+ .define_method("possible_for_type?", &parsed_number_possible_for_type)
912
+ .define_method("timezones", &parsed_number_timezones);
864
913
 
865
914
  rb_define_alloc_func(rb_cPhoneNumber, rb_phone_number_alloc);
866
915
  rb_define_method(rb_cPhoneNumber, "initialize", reinterpret_cast<VALUE (*)(...)>(phone_number_initialize), -1);
867
916
  rb_define_method(rb_cPhoneNumber, "geo_name", reinterpret_cast<VALUE (*)(...)>(parsed_number_geo_name), -1);
917
+ rb_define_method(rb_cPhoneNumber, "carrier_name", reinterpret_cast<VALUE (*)(...)>(parsed_number_carrier_name), -1);
868
918
  }