ffi-icu 0.6.1 → 0.8.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.
@@ -3,28 +3,28 @@
3
3
  module ICU
4
4
  module Normalization
5
5
  def self.normalize(input, mode = :default)
6
- input_length = input.size
7
6
  needed_length = out_length = options = 0
8
- in_ptr = UCharPointer.from_string(input)
7
+ input_pointer = UCharPointer.from_utf8(input)
8
+ input_length = input_pointer.length_in_uchars
9
9
  out_ptr = UCharPointer.new(out_length)
10
10
 
11
11
  retried = false
12
12
 
13
13
  begin
14
14
  Lib.check_error do |error|
15
- needed_length = Lib.unorm_normalize(in_ptr, input_length, mode, options, out_ptr, out_length, error)
15
+ needed_length = Lib.unorm_normalize(input_pointer, input_length, mode, options, out_ptr, out_length, error)
16
16
  end
17
17
  rescue BufferOverflowError
18
18
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
19
19
 
20
- out_ptr = out_ptr.resized_to(needed_length)
21
20
  out_length = needed_length + 1
21
+ out_ptr = out_ptr.resized_to(out_length)
22
22
 
23
23
  retried = true
24
24
  retry
25
25
  end
26
26
 
27
- out_ptr.string
27
+ out_ptr.utf8_string(needed_length)
28
28
  end
29
29
  end
30
30
  end
@@ -11,39 +11,38 @@ module ICU
11
11
  end
12
12
 
13
13
  def normalize(input)
14
- input_length = input.size
15
- in_ptr = UCharPointer.from_string(input)
14
+ input_pointer = UCharPointer.from_utf8(input)
15
+ input_length = input_pointer.length_in_uchars
16
16
  needed_length = capacity = 0
17
17
  out_ptr = UCharPointer.new(needed_length)
18
18
 
19
19
  retried = false
20
20
  begin
21
21
  Lib.check_error do |error|
22
- needed_length = Lib.unorm2_normalize(@instance, in_ptr, input_length, out_ptr, capacity, error)
22
+ needed_length = Lib.unorm2_normalize(@instance, input_pointer, input_length, out_ptr, capacity, error)
23
23
  end
24
24
  rescue BufferOverflowError
25
25
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
26
26
 
27
- capacity = needed_length
28
- out_ptr = out_ptr.resized_to(needed_length)
27
+ capacity = needed_length + 1
28
+ out_ptr = out_ptr.resized_to(capacity)
29
29
 
30
30
  retried = true
31
31
  retry
32
32
  end
33
33
 
34
- out_ptr.string
34
+ out_ptr.utf8_string(needed_length)
35
35
  end
36
36
 
37
- def normailzed?(input)
38
- input_length = input.size
39
- in_ptr = UCharPointer.from_string(input)
37
+ def normalized?(input)
38
+ input_pointer = UCharPointer.from_utf8(input)
39
+ input_length = input_pointer.length_in_uchars
40
40
 
41
41
  Lib.check_error do |error|
42
- Lib.unorm2_isNormalized(@instance, in_ptr, input_length, error)
42
+ Lib.unorm2_isNormalized(@instance, input_pointer, input_length, error)
43
43
  end
44
-
45
- result
46
44
  end
45
+ alias normailzed? normalized?
47
46
 
48
47
  def is_normalized?(input) # rubocop:disable Naming/PredicatePrefix
49
48
  Warning.warn('is_normalized? is deprecated and will be removed after v0.7. Please use normalized? instead.')
@@ -15,6 +15,10 @@ module ICU
15
15
  end
16
16
  end
17
17
 
18
+ def self.create_with_skeleton(locale, skeleton)
19
+ SkeletonFormatter.new(locale, skeleton)
20
+ end
21
+
18
22
  def self.clear_default_options
19
23
  @default_options.clear
20
24
  end
@@ -35,6 +39,10 @@ module ICU
35
39
  create(locale, :currency, options).format(number, currency)
36
40
  end
37
41
 
42
+ def self.format_with_skeleton(locale, number, skeleton)
43
+ create_with_skeleton(locale, skeleton).format(number)
44
+ end
45
+
38
46
  def self.spell(locale, number, options = {})
39
47
  create(locale, :spellout, options).format(number)
40
48
  end
@@ -48,6 +56,11 @@ module ICU
48
56
  private
49
57
 
50
58
  def make_formatter(type, locale)
59
+ if type == :pattern_rule_based
60
+ raise(ArgumentError, 'The pattern_rule_based style needs custom rules, which ffi-icu does not support; ' \
61
+ 'ICU::NumberFormatting.create_with_skeleton covers most other formats')
62
+ end
63
+
51
64
  ptr = Lib.check_error do |error|
52
65
  Lib.unum_open(type, FFI::MemoryPointer.new(4), 0, locale, FFI::MemoryPointer.new(4), error)
53
66
  end
@@ -97,7 +110,7 @@ module ICU
97
110
  end
98
111
  end
99
112
  end
100
- out_ptr.string(needed_length)
113
+ out_ptr.utf8_string(needed_length)
101
114
  rescue BufferOverflowError
102
115
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
103
116
 
@@ -136,10 +149,10 @@ module ICU
136
149
 
137
150
  begin
138
151
  Lib.check_error do |error|
139
- needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_string(currency, 4), out_ptr,
152
+ needed_length = Lib.unum_format_currency(@f, number, UCharPointer.from_utf8(currency, 4), out_ptr,
140
153
  needed_length, nil, error)
141
154
  end
142
- out_ptr.string
155
+ out_ptr.utf8_string
143
156
  rescue BufferOverflowError
144
157
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
145
158
 
@@ -149,5 +162,33 @@ module ICU
149
162
  end
150
163
  end
151
164
  end
165
+
166
+ class SkeletonFormatter
167
+ def initialize(locale, skeleton)
168
+ unless Lib.respond_to?(:unumf_openForSkeletonAndLocale)
169
+ raise('ICU::NumberFormatting::SkeletonFormatter requires ICU >= 62')
170
+ end
171
+
172
+ skeleton_uchar = UCharPointer.from_utf8(skeleton)
173
+ @f = FFI::AutoPointer.new(
174
+ Lib.check_error do |error|
175
+ Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars, locale, error)
176
+ end,
177
+ Lib.method(:unumf_close)
178
+ )
179
+ end
180
+
181
+ def format(number)
182
+ decimal = number.to_s
183
+ result = FFI::AutoPointer.new(
184
+ Lib.check_error { |error| Lib.unumf_openResult(error) },
185
+ Lib.method(:unumf_closeResult)
186
+ )
187
+ Lib.check_error { |error| Lib.unumf_formatDecimal(@f, decimal, decimal.bytesize, result, error) }
188
+ Lib::Util.read_uchar_buffer(0) do |buffer, error|
189
+ Lib.unumf_resultToString(result, buffer, buffer.length_in_uchars, error)
190
+ end
191
+ end
192
+ end
152
193
  end
153
194
  end
@@ -91,8 +91,8 @@ module ICU
91
91
  pattern_ptr = FFI::MemoryPointer.new(4)
92
92
 
93
93
  if time_zone_str
94
- time_zone = UCharPointer.from_string(time_zone_str)
95
- tz_len = time_zone_str.size
94
+ time_zone = UCharPointer.from_utf8(time_zone_str)
95
+ tz_len = time_zone.length_in_uchars
96
96
  else
97
97
  Lib.check_error do |error|
98
98
  i_len = 150
@@ -142,8 +142,8 @@ module ICU
142
142
  end
143
143
 
144
144
  def parse(str)
145
- str_u = UCharPointer.from_string(str)
146
- str_l = str.size
145
+ str_u = UCharPointer.from_utf8(str)
146
+ str_l = str_u.length_in_uchars
147
147
  Lib.check_error do |error|
148
148
  ret = Lib.udat_parse(@f, str_u, str_l, nil, error)
149
149
  Time.at(ret / 1000.0)
@@ -170,7 +170,7 @@ module ICU
170
170
  end
171
171
  end
172
172
 
173
- out_ptr.string
173
+ out_ptr.utf8_string
174
174
  rescue BufferOverflowError
175
175
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
176
176
 
@@ -209,7 +209,7 @@ module ICU
209
209
  needed_length = Lib.udat_toPattern(@f, localized, out_ptr, needed_length, error)
210
210
  end
211
211
 
212
- out_ptr.string
212
+ out_ptr.utf8_string
213
213
  rescue BufferOverflowError
214
214
  raise(BufferOverflowError, "needed: #{needed_length}") if retried
215
215
 
@@ -228,8 +228,8 @@ module ICU
228
228
  end
229
229
 
230
230
  def skeleton_format(skeleton_pattern_str, locale)
231
- skeleton_pattern_ptr = UCharPointer.from_string(skeleton_pattern_str)
232
- skeleton_pattern_len = skeleton_pattern_str.size
231
+ skeleton_pattern_ptr = UCharPointer.from_utf8(skeleton_pattern_str)
232
+ skeleton_pattern_len = skeleton_pattern_ptr.length_in_uchars
233
233
 
234
234
  needed_length = 0
235
235
  pattern_ptr = UCharPointer.new(needed_length)
@@ -272,7 +272,7 @@ module ICU
272
272
  return unless @hour_cycle
273
273
 
274
274
  # Get the current pattern and convert to a skeleton
275
- skeleton_str = pattern_to_skeleton_uchar(current_pattern_as_uchar).string
275
+ skeleton_str = pattern_to_skeleton_uchar(current_pattern_as_uchar).utf8_string
276
276
 
277
277
  # Manipulate the skeleton to make it work with the correct hour cycle.
278
278
  skeleton_str.gsub!(/[hHkKjJ]/, HOUR_CYCLE_SYMS[@hour_cycle])
@@ -286,7 +286,7 @@ module ICU
286
286
  end
287
287
 
288
288
  # Convert the skeleton back to a pattern
289
- new_pattern_str = skeleton_to_pattern_uchar(UCharPointer.from_string(skeleton_str)).string
289
+ new_pattern_str = skeleton_to_pattern_uchar(UCharPointer.from_utf8(skeleton_str)).utf8_string
290
290
 
291
291
  # We also need to manipulate the _pattern_, a little bit, because (according to Firefox source):
292
292
  #
@@ -345,8 +345,8 @@ module ICU
345
345
  end
346
346
 
347
347
  def set_date_format_impl(localized, pattern_str)
348
- pattern = UCharPointer.from_string(pattern_str)
349
- pattern_len = pattern_str.size
348
+ pattern = UCharPointer.from_utf8(pattern_str)
349
+ pattern_len = pattern.length_in_uchars
350
350
 
351
351
  Lib.check_error do |_error|
352
352
  Lib.udat_applyPattern(@f, localized, pattern, pattern_len)
@@ -26,15 +26,17 @@ module ICU
26
26
  rules_length = 0
27
27
 
28
28
  if rules
29
- rules_length = rules.size + 1
30
- rules = UCharPointer.from_string(rules)
29
+ rules = UCharPointer.from_utf8(rules)
30
+ rules_length = rules.length_in_uchars
31
31
  end
32
32
 
33
+ id = UCharPointer.from_utf8(id)
34
+
33
35
  parse_error = Lib::UParseError.new
34
36
  begin
35
37
  Lib.check_error do |status|
36
- ptr = Lib.utrans_openU(UCharPointer.from_string(id), id.size, direction, rules, rules_length,
37
- @parse_error, status)
38
+ ptr = Lib.utrans_openU(id, id.length_in_uchars, direction, rules, rules_length,
39
+ parse_error, status)
38
40
  @tr = FFI::AutoPointer.new(ptr, Lib.method(:utrans_close))
39
41
  end
40
42
  rescue ICU::Error => e
@@ -45,9 +47,11 @@ module ICU
45
47
  def transliterate(from)
46
48
  # this is a bit unpleasant
47
49
 
48
- unicode_size = from.unpack('U*').size
49
- capacity = unicode_size + 1
50
- buf = UCharPointer.from_string(from, capacity)
50
+ input = UCharPointer.from_utf8(from)
51
+ input_length_in_uchars = input.length_in_uchars
52
+ capacity = input_length_in_uchars + 1
53
+ buf = UCharPointer.new(capacity)
54
+ buf.put_bytes(0, input.get_bytes(0, input.size))
51
55
  limit = FFI::MemoryPointer.new(:int32)
52
56
  text_length = FFI::MemoryPointer.new(:int32)
53
57
 
@@ -56,7 +60,7 @@ module ICU
56
60
  begin
57
61
  # resets to original size on retry
58
62
  [limit, text_length].each do |ptr|
59
- ptr.put_int32(0, unicode_size)
63
+ ptr.put_int32(0, input_length_in_uchars)
60
64
  end
61
65
 
62
66
  Lib.check_error do |error|
@@ -73,13 +77,13 @@ module ICU
73
77
  # create a new buffer with more capacity instead of resizing,
74
78
  # since the old buffer now has result data
75
79
  buf.free
76
- buf = UCharPointer.from_string(from, capacity)
80
+ buf = UCharPointer.from_utf8(from, capacity)
77
81
 
78
82
  retried = true
79
83
  retry
80
84
  end
81
85
 
82
- buf.string(text_length.get_int32(0))
86
+ buf.utf8_string(text_length.get_int32(0))
83
87
  end
84
88
  end
85
89
  end
data/lib/ffi-icu/uchar.rb CHANGED
@@ -22,6 +22,22 @@ module ICU
22
22
  ptr
23
23
  end
24
24
 
25
+ def self.from_utf8(str, capacity = nil)
26
+ chars = str.encode(Encoding::UTF_16LE).unpack('v*')
27
+
28
+ if capacity
29
+ raise(ArgumentError, "capacity is too small for string of #{chars.size} UChars") if capacity < chars.size
30
+
31
+ ptr = new(capacity)
32
+ else
33
+ ptr = new(chars.size)
34
+ end
35
+
36
+ ptr.write_array_of_uint16(chars)
37
+
38
+ ptr
39
+ end
40
+
25
41
  def initialize(size)
26
42
  super(UCHAR_TYPE, size)
27
43
  end
@@ -42,6 +58,13 @@ module ICU
42
58
  wstring.pack('U*')
43
59
  end
44
60
 
61
+ def utf8_string(length = nil)
62
+ length ||= size / TYPE_SIZE
63
+
64
+ wstring = read_array_of_uint16(length).pack('v*')
65
+ wstring.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8)
66
+ end
67
+
45
68
  def length_in_uchars
46
69
  size / type_size
47
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ICU
4
- VERSION = '0.6.1'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/ffi-icu.rb CHANGED
@@ -25,11 +25,14 @@ require 'ffi-icu/lib/util'
25
25
  require 'ffi-icu/uchar'
26
26
  require 'ffi-icu/chardet'
27
27
  require 'ffi-icu/collation'
28
+ require 'ffi-icu/currency'
28
29
  require 'ffi-icu/locale'
29
30
  require 'ffi-icu/transliteration'
30
31
  require 'ffi-icu/normalization'
31
32
  require 'ffi-icu/normalizer'
32
33
  require 'ffi-icu/break_iterator'
33
34
  require 'ffi-icu/number_formatting'
35
+ require 'ffi-icu/message_formatting'
34
36
  require 'ffi-icu/time_formatting'
35
37
  require 'ffi-icu/duration_formatting'
38
+ require 'ffi-icu/list_formatting'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-icu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erick Guan
@@ -76,10 +76,13 @@ files:
76
76
  - lib/ffi-icu/break_iterator.rb
77
77
  - lib/ffi-icu/chardet.rb
78
78
  - lib/ffi-icu/collation.rb
79
+ - lib/ffi-icu/currency.rb
79
80
  - lib/ffi-icu/duration_formatting.rb
80
81
  - lib/ffi-icu/lib.rb
81
82
  - lib/ffi-icu/lib/util.rb
83
+ - lib/ffi-icu/list_formatting.rb
82
84
  - lib/ffi-icu/locale.rb
85
+ - lib/ffi-icu/message_formatting.rb
83
86
  - lib/ffi-icu/normalization.rb
84
87
  - lib/ffi-icu/normalizer.rb
85
88
  - lib/ffi-icu/number_formatting.rb