ffi-icu 0.5.3 → 0.6.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -0
  3. data/LICENSE +1 -1
  4. data/README.md +21 -51
  5. data/Rakefile +4 -5
  6. data/ffi-icu.gemspec +34 -25
  7. data/lib/ffi-icu/break_iterator.rb +19 -18
  8. data/lib/ffi-icu/chardet.rb +12 -13
  9. data/lib/ffi-icu/collation.rb +62 -59
  10. data/lib/ffi-icu/duration_formatting.rb +293 -267
  11. data/lib/ffi-icu/lib/util.rb +10 -10
  12. data/lib/ffi-icu/lib.rb +273 -202
  13. data/lib/ffi-icu/locale.rb +12 -8
  14. data/lib/ffi-icu/normalization.rb +7 -7
  15. data/lib/ffi-icu/normalizer.rb +14 -8
  16. data/lib/ffi-icu/number_formatting.rb +41 -27
  17. data/lib/ffi-icu/time_formatting.rb +116 -93
  18. data/lib/ffi-icu/transliteration.rb +19 -19
  19. data/lib/ffi-icu/uchar.rb +14 -17
  20. data/lib/ffi-icu/version.rb +3 -1
  21. data/lib/ffi-icu.rb +16 -17
  22. metadata +35 -71
  23. data/.document +0 -5
  24. data/.gitignore +0 -23
  25. data/.rspec +0 -2
  26. data/.travis.yml +0 -28
  27. data/benchmark/detect.rb +0 -14
  28. data/benchmark/shared.rb +0 -17
  29. data/build_icu.sh +0 -53
  30. data/lib/ffi-icu/core_ext/string.rb +0 -9
  31. data/spec/break_iterator_spec.rb +0 -77
  32. data/spec/chardet_spec.rb +0 -42
  33. data/spec/collation_spec.rb +0 -84
  34. data/spec/duration_formatting_spec.rb +0 -143
  35. data/spec/lib/version_info_spec.rb +0 -20
  36. data/spec/lib_spec.rb +0 -63
  37. data/spec/locale_spec.rb +0 -280
  38. data/spec/normalization_spec.rb +0 -22
  39. data/spec/normalizer_spec.rb +0 -57
  40. data/spec/number_formatting_spec.rb +0 -79
  41. data/spec/spec_helper.rb +0 -13
  42. data/spec/time_spec.rb +0 -198
  43. data/spec/transliteration_spec.rb +0 -36
  44. data/spec/uchar_spec.rb +0 -34
  45. data/test.c +0 -56
@@ -1,282 +1,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
1
5
  module ICU
2
- module DurationFormatting
3
- VALID_FIELDS = %i[
4
- years
5
- months
6
- weeks
7
- days
8
- hours
9
- minutes
10
- seconds
11
- milliseconds
12
- microseconds
13
- nanoseconds
14
- ]
15
- HMS_FIELDS = %i[
16
- hours
17
- minutes
18
- seconds
19
- milliseconds
20
- microseconds
21
- nanoseconds
22
- ]
23
- ROUNDABLE_FIELDS = %i[
24
- seconds
25
- milliseconds
26
- microseconds
27
- nanoseconds
28
- ]
29
- VALID_STYLES = %i[long short narrow digital]
30
- STYLES_TO_LIST_JOIN_FORMAT = {
31
- long: :wide,
32
- short: :short,
33
- narrow: :narrow,
34
- digital: :narrow,
35
- }
36
- UNIT_FORMAT_STRINGS = {
37
- years: 'measure-unit/duration-year',
38
- months: 'measure-unit/duration-month',
39
- weeks: 'measure-unit/duration-week',
40
- days: 'measure-unit/duration-day',
41
- hours: 'measure-unit/duration-hour',
42
- minutes: 'measure-unit/duration-minute',
43
- seconds: 'measure-unit/duration-second',
44
- milliseconds: 'measure-unit/duration-millisecond',
45
- microseconds: 'measure-unit/duration-microsecond',
46
- nanoseconds: 'measure-unit/duration-nanosecond',
47
- }
48
- STYLES_TO_NUMBER_FORMAT_WIDTH = {
49
- long: 'unit-width-full-name',
50
- short: 'unit-width-short',
51
- narrow: 'unit-width-narrow',
52
- # digital for hours:minutes:seconds has some special casing.
53
- digital: 'unit-width-narrow',
54
- }
55
-
56
- def self.format(fields, locale:, style: :long)
57
- DurationFormatter.new(locale: locale, style: style).format(fields)
6
+ module DurationFormatting
7
+ VALID_FIELDS = [
8
+ :years,
9
+ :months,
10
+ :weeks,
11
+ :days,
12
+ :hours,
13
+ :minutes,
14
+ :seconds,
15
+ :milliseconds,
16
+ :microseconds,
17
+ :nanoseconds
18
+ ].freeze
19
+
20
+ HMS_FIELDS = [
21
+ :hours,
22
+ :minutes,
23
+ :seconds,
24
+ :milliseconds,
25
+ :microseconds,
26
+ :nanoseconds
27
+ ].freeze
28
+
29
+ ROUNDABLE_FIELDS = [
30
+ :seconds,
31
+ :milliseconds,
32
+ :microseconds,
33
+ :nanoseconds
34
+ ].freeze
35
+
36
+ VALID_STYLES = [:long, :short, :narrow, :digital].freeze
37
+
38
+ STYLES_TO_LIST_JOIN_FORMAT = {
39
+ :long => :wide,
40
+ :short => :short,
41
+ :narrow => :narrow,
42
+ :digital => :narrow
43
+ }.freeze
44
+
45
+ UNIT_FORMAT_STRINGS = {
46
+ :years => 'measure-unit/duration-year',
47
+ :months => 'measure-unit/duration-month',
48
+ :weeks => 'measure-unit/duration-week',
49
+ :days => 'measure-unit/duration-day',
50
+ :hours => 'measure-unit/duration-hour',
51
+ :minutes => 'measure-unit/duration-minute',
52
+ :seconds => 'measure-unit/duration-second',
53
+ :milliseconds => 'measure-unit/duration-millisecond',
54
+ :microseconds => 'measure-unit/duration-microsecond',
55
+ :nanoseconds => 'measure-unit/duration-nanosecond'
56
+ }.freeze
57
+
58
+ STYLES_TO_NUMBER_FORMAT_WIDTH = {
59
+ :long => 'unit-width-full-name',
60
+ :short => 'unit-width-short',
61
+ :narrow => 'unit-width-narrow',
62
+ # digital for hours:minutes:seconds has some special casing.
63
+ :digital => 'unit-width-narrow'
64
+ }.freeze
65
+
66
+ def self.format(fields, locale:, style: :long)
67
+ DurationFormatter.new(locale:, style:).format(fields)
68
+ end
69
+
70
+ class DurationFormatter
71
+ def initialize(locale:, style: :long)
72
+ if !Lib.respond_to?(:unumf_openForSkeletonAndLocale) || !Lib.respond_to?(:ulistfmt_openForType)
73
+ raise('ICU::DurationFormatting requires ICU >= 67')
58
74
  end
59
75
 
60
- class DurationFormatter
61
- def initialize(locale:, style: :long)
62
- if !Lib.respond_to?(:unumf_openForSkeletonAndLocale) || !Lib.respond_to?(:ulistfmt_openForType)
63
- raise "ICU::DurationFormatting requires ICU >= 67"
64
- end
65
-
66
- raise ArgumentError, "Unknown style #{style}" unless VALID_STYLES.include?(style)
67
-
68
- @locale = locale
69
- @style = style
70
- # These are created lazily based on what parts are actually included
71
- @number_formatters = {}
72
-
73
- list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style)
74
- @list_formatter = FFI::AutoPointer.new(
75
- Lib.check_error { |error|
76
- Lib.ulistfmt_openForType(@locale, :units, list_join_format, error)
77
- },
78
- Lib.method(:ulistfmt_close)
79
- )
80
- end
76
+ raise(ArgumentError, "Unknown style #{style}") unless VALID_STYLES.include?(style)
81
77
 
82
- def format(fields)
83
- fields.each_key do |field|
84
- raise "Unknown field #{field}" unless VALID_FIELDS.include?(field)
85
- end
86
- fields = fields.dup # we might modify this argument.
87
-
88
- # Intl.js spec says that rounding options affect only the smallest unit, and only
89
- # if that unit is sub-second. All other fields therefore need to be truncated.
90
- smallest_unit = VALID_FIELDS[fields.keys.map { |k| VALID_FIELDS.index(k) }.max]
91
- fields.each_key do |k|
92
- raise ArgumentError, "Negative durations are not yet supported" if fields[k] < 0
93
- fields[k] = fields[k].to_i unless k == smallest_unit && ROUNDABLE_FIELDS.include?(smallest_unit)
94
- end
95
-
96
- formatted_hms = nil
97
- if @style == :digital
98
- # icu::MeasureFormat contains special casing for hours/minutes/seconds formatted
99
- # at numeric width, to render it as h:mm:s, essentially. This involves using
100
- # a pattern called durationUnits defined in the ICU data for the locale.
101
- # If we have data for this combination of hours/mins/seconds in this locale,
102
- # use that and emulate ICU's special casing.
103
- formatted_hms = format_hms(fields)
104
- if formatted_hms
105
- # We've taken care of all these fields now.
106
- HMS_FIELDS.each do |f|
107
- fields.delete f
108
- end
109
- end
110
- end
111
-
112
- formatted_fields = VALID_FIELDS.map do |f|
113
- next unless fields.key?(f)
114
- next unless fields[f] != 0
115
-
116
- format_number(fields[f], [
117
- UNIT_FORMAT_STRINGS[f], STYLES_TO_NUMBER_FORMAT_WIDTH[@style],
118
- (".#########" if f == smallest_unit),
119
- ].compact.join(' '))
120
- end
121
- formatted_fields << formatted_hms
122
- formatted_fields.compact!
123
-
124
- format_list(formatted_fields)
125
- end
78
+ @locale = locale
79
+ @style = style
80
+ # These are created lazily based on what parts are actually included
81
+ @number_formatters = {}
126
82
 
127
- private
128
-
129
- def hms_duration_units_pattern(fields)
130
- return nil unless HMS_FIELDS.any? { |k| fields.key?(k) }
131
- @unit_res_bundle ||= FFI::AutoPointer.new(
132
- Lib.check_error { |error| Lib.ures_open(Lib.resource_bundle_name(:unit), @locale, error) },
133
- Lib.method(:ures_close)
134
- )
135
-
136
- resource_key = "durationUnits/"
137
- resource_key << "h" if fields.key?(:hours)
138
- resource_key << "m" if fields.key?(:minutes)
139
- resource_key << "s" if [:seconds, :milliseconds, :microseconds, :nanoseconds].any? { |f| fields.key?(f) }
140
-
141
- begin
142
- pattern_resource = FFI::AutoPointer.new(
143
- Lib.check_error { |error| Lib.ures_getBykeyWithFallback(@unit_res_bundle, resource_key, nil, error) },
144
- Lib.method(:ures_close)
145
- )
146
- rescue MissingResourceError
147
- # This combination of h,m,s not present for this locale.
148
- return nil
149
- end
150
- # Read the resource as a UChar (whose memory we _do not own_ - it's static data) and
151
- # convert it to a Ruby string.
152
- pattern_uchar_len = FFI::MemoryPointer.new(:int32_t)
153
- pattern_uchar = Lib.check_error { |error|
154
- Lib.ures_getString(pattern_resource, pattern_uchar_len, error)
155
- }
156
- pattern_str = pattern_uchar.read_array_of_uint16(pattern_uchar_len.read_int32).pack("U*")
157
-
158
- # For some reason I can't comprehend, loadNumericDateFormatterPattern in ICU wants to turn
159
- # h's into H's here. I guess we have to do it too because the pattern data could in theory
160
- # now contain either.
161
- pattern_str.gsub('h', 'H')
162
- end
83
+ list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style)
84
+ @list_formatter = FFI::AutoPointer.new(
85
+ Lib.check_error do |error|
86
+ Lib.ulistfmt_openForType(@locale, :units, list_join_format, error)
87
+ end,
88
+ Lib.method(:ulistfmt_close)
89
+ )
90
+ end
163
91
 
164
- def format_hms(fields)
165
- pattern = hms_duration_units_pattern(fields)
166
- return nil if pattern.nil?
167
-
168
- # According to the Intl.js spec, when formatting in digital, everything < seconds
169
- # should be coalesced into decimal seconds
170
- seconds_incl_fractional = fields.fetch(:seconds, 0)
171
- second_precision = 0
172
- if fields.key?(:milliseconds)
173
- seconds_incl_fractional += fields[:milliseconds] / 1e3
174
- second_precision = 3
175
- end
176
- if fields.key?(:microseconds)
177
- seconds_incl_fractional += fields[:microseconds] / 1e6
178
- second_precision = 6
179
- end
180
- if fields.key?(:nanoseconds)
181
- seconds_incl_fractional += fields[:nanoseconds] / 1e9
182
- second_precision = 9
183
- end
184
-
185
- # Follow the rules in ICU measfmt.cpp formatNumeric to fill in the patterns here with
186
- # the appropriate values.
187
- enum = pattern.each_char
188
- protect = false
189
- result = ""
190
- loop do
191
- char = enum.next
192
- next_char = enum.peek rescue nil
193
-
194
- if protect
195
- # In literal mode
196
- if char == "'"
197
- protect = false
198
- next
199
- end
200
- result << char
201
- next
202
- end
203
-
204
- value = case char
205
- when 'H' then fields[:hours]
206
- when 'm' then fields[:minutes]
207
- when 's' then seconds_incl_fractional
208
- end
209
-
210
- case char
211
- when 'H', 'm', 's'
212
- skeleton = "."
213
- if char == 's' && second_precision > 0
214
- skeleton << ("0" * second_precision)
215
- else
216
- skeleton << ("#" * 9)
217
- end
218
- if char == next_char
219
- # It's doubled - means format it at zero fill
220
- skeleton << " integer-width/00"
221
- enum.next
222
- end
223
- result << format_number(value, skeleton)
224
- when "'"
225
- if next_char == char
226
- # double-apostrophe, means literal '
227
- result << "'"
228
- enum.next
229
- else
230
- protect = true
231
- end
232
- else
233
- result << char
234
- end
235
- end
236
-
237
- result
238
- end
92
+ def format(fields)
93
+ fields.each_key do |field|
94
+ raise("Unknown field #{field}") unless VALID_FIELDS.include?(field)
95
+ end
96
+ fields = fields.dup # we might modify this argument.
97
+
98
+ # Intl.js spec says that rounding options affect only the smallest unit, and only
99
+ # if that unit is sub-second. All other fields therefore need to be truncated.
100
+ smallest_unit = VALID_FIELDS[fields.keys.map { |k| VALID_FIELDS.index(k) }.max]
101
+ fields.each_key do |k|
102
+ raise(ArgumentError, 'Negative durations are not yet supported') if fields[k].negative?
239
103
 
240
- def number_formatter(skeleton)
241
- @number_formatters[skeleton] ||= begin
242
- skeleton_uchar = UCharPointer.from_string(skeleton)
243
- FFI::AutoPointer.new(
244
- Lib.check_error { |error|
245
- Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars, @locale, error)
246
- },
247
- Lib.method(:unumf_close)
248
- )
249
- end
104
+ fields[k] = fields[k].to_i unless k == smallest_unit && ROUNDABLE_FIELDS.include?(smallest_unit)
105
+ end
106
+
107
+ formatted_hms = nil
108
+ if @style == :digital
109
+ # icu::MeasureFormat contains special casing for hours/minutes/seconds formatted
110
+ # at numeric width, to render it as h:mm:s, essentially. This involves using
111
+ # a pattern called durationUnits defined in the ICU data for the locale.
112
+ # If we have data for this combination of hours/mins/seconds in this locale,
113
+ # use that and emulate ICU's special casing.
114
+ formatted_hms = format_hms(fields)
115
+ if formatted_hms
116
+ # We've taken care of all these fields now.
117
+ HMS_FIELDS.each do |f|
118
+ fields.delete(f)
250
119
  end
120
+ end
121
+ end
122
+
123
+ formatted_fields = VALID_FIELDS.map do |f|
124
+ next unless fields.key?(f)
125
+ next unless fields[f] != 0
126
+
127
+ format_number(fields[f], [
128
+ UNIT_FORMAT_STRINGS[f], STYLES_TO_NUMBER_FORMAT_WIDTH[@style],
129
+ ('.#########' if f == smallest_unit)
130
+ ].compact.join(' '))
131
+ end
132
+ formatted_fields << formatted_hms
133
+ formatted_fields.compact!
134
+
135
+ format_list(formatted_fields)
136
+ end
137
+
138
+ private
139
+
140
+ def hms_duration_units_pattern(fields)
141
+ return nil unless HMS_FIELDS.any? { |k| fields.key?(k) }
251
142
 
252
- def format_number(value, skeleton)
253
- formatter = number_formatter(skeleton)
254
- result = FFI::AutoPointer.new(
255
- Lib.check_error { |error| Lib.unumf_openResult(error) },
256
- Lib.method(:unumf_closeResult)
257
- )
258
- value_str = value.to_s
259
- Lib.check_error do |error|
260
- Lib.unumf_formatDecimal(formatter, value_str, value_str.size, result, error)
261
- end
262
- Lib::Util.read_uchar_buffer(0) do |buf, error|
263
- Lib.unumf_resultToString(result, buf, buf.length_in_uchars, error)
264
- end
143
+ @unit_res_bundle ||= FFI::AutoPointer.new(
144
+ Lib.check_error { |error| Lib.ures_open(Lib.resource_bundle_name(:unit), @locale, error) },
145
+ Lib.method(:ures_close)
146
+ )
147
+
148
+ resource_key_builder = StringIO.new
149
+ resource_key_builder.write('durationUnits/')
150
+ resource_key_builder.putc('h') if fields.key?(:hours)
151
+ resource_key_builder.putc('m') if fields.key?(:minutes)
152
+ if [:seconds, :milliseconds, :microseconds, :nanoseconds].any? { |f| fields.key?(f) }
153
+ resource_key_builder.putc('s')
154
+ end
155
+
156
+ resource_key = resource_key_builder.string
157
+
158
+ begin
159
+ pattern_resource = FFI::AutoPointer.new(
160
+ Lib.check_error do |error|
161
+ Lib.ures_getBykeyWithFallback(@unit_res_bundle, resource_key, nil, error)
162
+ end,
163
+ Lib.method(:ures_close)
164
+ )
165
+ rescue MissingResourceError
166
+ # This combination of h,m,s not present for this locale.
167
+ return nil
168
+ end
169
+ # Read the resource as a UChar (whose memory we _do not own_ - it's static data) and
170
+ # convert it to a Ruby string.
171
+ pattern_uchar_len = FFI::MemoryPointer.new(:int32_t)
172
+ pattern_uchar = Lib.check_error do |error|
173
+ Lib.ures_getString(pattern_resource, pattern_uchar_len, error)
174
+ end
175
+ pattern_str = pattern_uchar.read_array_of_uint16(pattern_uchar_len.read_int32).pack('U*')
176
+
177
+ # For some reason I can't comprehend, loadNumericDateFormatterPattern in ICU wants to turn
178
+ # h's into H's here. I guess we have to do it too because the pattern data could in theory
179
+ # now contain either.
180
+ pattern_str.gsub('h', 'H')
181
+ end
182
+
183
+ def format_hms(fields)
184
+ pattern = hms_duration_units_pattern(fields)
185
+ return nil if pattern.nil?
186
+
187
+ # According to the Intl.js spec, when formatting in digital, everything < seconds
188
+ # should be coalesced into decimal seconds
189
+ seconds_incl_fractional = fields.fetch(:seconds, 0)
190
+ second_precision = 0
191
+ if fields.key?(:milliseconds)
192
+ seconds_incl_fractional += fields[:milliseconds] / 1e3
193
+ second_precision = 3
194
+ end
195
+ if fields.key?(:microseconds)
196
+ seconds_incl_fractional += fields[:microseconds] / 1e6
197
+ second_precision = 6
198
+ end
199
+ if fields.key?(:nanoseconds)
200
+ seconds_incl_fractional += fields[:nanoseconds] / 1e9
201
+ second_precision = 9
202
+ end
203
+
204
+ # Follow the rules in ICU measfmt.cpp formatNumeric to fill in the patterns here with
205
+ # the appropriate values.
206
+ enum = pattern.each_char
207
+ protect = false
208
+ result = StringIO.new
209
+ loop do
210
+ char = enum.next
211
+ next_char = begin
212
+ enum.peek
213
+ rescue StandardError
214
+ nil
215
+ end
216
+
217
+ if protect
218
+ # In literal mode
219
+ if char == "'"
220
+ protect = false
221
+ next
265
222
  end
223
+ result.write(char)
224
+ next
225
+ end
226
+
227
+ value = case char
228
+ when 'H' then fields[:hours]
229
+ when 'm' then fields[:minutes]
230
+ when 's' then seconds_incl_fractional
231
+ end
266
232
 
267
- def format_list(values)
268
- value_uchars = values.map(&UCharPointer.method(:from_string))
269
- value_uchars_array = FFI::MemoryPointer.new(:pointer, value_uchars.size)
270
- value_uchars_array.put_array_of_pointer(0, value_uchars)
271
- value_lengths_array = FFI::MemoryPointer.new(:int32_t, value_uchars.size)
272
- value_lengths_array.put_array_of_int32(0, value_uchars.map(&:length_in_uchars))
273
- Lib::Util.read_uchar_buffer(0) do |buf, error|
274
- Lib.ulistfmt_format(
275
- @list_formatter, value_uchars_array, value_lengths_array,
276
- value_uchars.size, buf, buf.length_in_uchars, error
277
- )
278
- end
233
+ case char
234
+ when 'H', 'm', 's'
235
+ skeleton_builder = StringIO.new
236
+ skeleton_builder.write('.')
237
+ if char == 's' && second_precision.positive?
238
+ skeleton_builder.write('0' * second_precision)
239
+ else
240
+ skeleton_builder.write('#' * 9)
241
+ end
242
+ if char == next_char
243
+ # It's doubled - means format it at zero fill
244
+ skeleton_builder.write(' integer-width/00')
245
+ enum.next
279
246
  end
247
+ skeleton = skeleton_builder.string
248
+ result.write(format_number(value, skeleton))
249
+ when "'"
250
+ if next_char == char
251
+ # double-apostrophe, means literal '
252
+ result.write("'")
253
+ enum.next
254
+ else
255
+ protect = true
256
+ end
257
+ else
258
+ result.write(char)
259
+ end
260
+ end
261
+
262
+ result.string
263
+ end
264
+
265
+ def number_formatter(skeleton)
266
+ @number_formatters[skeleton] ||= begin
267
+ skeleton_uchar = UCharPointer.from_string(skeleton)
268
+ FFI::AutoPointer.new(
269
+ Lib.check_error do |error|
270
+ Lib.unumf_openForSkeletonAndLocale(skeleton_uchar, skeleton_uchar.length_in_uchars,
271
+ @locale, error)
272
+ end,
273
+ Lib.method(:unumf_close)
274
+ )
275
+ end
276
+ end
277
+
278
+ def format_number(value, skeleton)
279
+ formatter = number_formatter(skeleton)
280
+ result = FFI::AutoPointer.new(
281
+ Lib.check_error { |error| Lib.unumf_openResult(error) },
282
+ Lib.method(:unumf_closeResult)
283
+ )
284
+ value_str = value.to_s
285
+ Lib.check_error do |error|
286
+ Lib.unumf_formatDecimal(formatter, value_str, value_str.size, result, error)
287
+ end
288
+ Lib::Util.read_uchar_buffer(0) do |buf, error|
289
+ Lib.unumf_resultToString(result, buf, buf.length_in_uchars, error)
290
+ end
291
+ end
292
+
293
+ def format_list(values)
294
+ value_uchars = values.map(&UCharPointer.method(:from_string))
295
+ value_uchars_array = FFI::MemoryPointer.new(:pointer, value_uchars.size)
296
+ value_uchars_array.put_array_of_pointer(0, value_uchars)
297
+ value_lengths_array = FFI::MemoryPointer.new(:int32_t, value_uchars.size)
298
+ value_lengths_array.put_array_of_int32(0, value_uchars.map(&:length_in_uchars))
299
+ Lib::Util.read_uchar_buffer(0) do |buf, error|
300
+ Lib.ulistfmt_format(
301
+ @list_formatter, value_uchars_array, value_lengths_array,
302
+ value_uchars.size, buf, buf.length_in_uchars, error
303
+ )
280
304
  end
305
+ end
281
306
  end
307
+ end
282
308
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ICU
2
4
  module Lib
3
5
  module Util
@@ -18,38 +20,36 @@ module ICU
18
20
 
19
21
  begin
20
22
  result = FFI::MemoryPointer.new(:char, length)
21
- Lib.check_error { |status| length = yield result, status }
23
+ Lib.check_error { |status| length = yield(result, status) }
22
24
  rescue BufferOverflowError
23
25
  attempts += 1
24
26
  retry if attempts < 2
25
- raise BufferOverflowError, "needed: #{length}"
27
+ raise(BufferOverflowError, "needed: #{length}")
26
28
  end
27
29
 
28
30
  result.read_string(length)
29
31
  end
30
32
 
31
- def self.read_uchar_buffer(length, &blk)
32
- buf, len = read_uchar_buffer_as_ptr_impl(length, &blk)
33
+ def self.read_uchar_buffer(length, &)
34
+ buf, len = read_uchar_buffer_as_ptr_impl(length, &)
33
35
  buf.string(len)
34
36
  end
35
37
 
36
- def self.read_uchar_buffer_as_ptr(length, &blk)
37
- buf, _ = read_uchar_buffer_as_ptr_impl(length, &blk)
38
+ def self.read_uchar_buffer_as_ptr(length, &)
39
+ buf, = read_uchar_buffer_as_ptr_impl(length, &)
38
40
  buf
39
41
  end
40
42
 
41
- private
42
-
43
43
  def self.read_uchar_buffer_as_ptr_impl(length)
44
44
  attempts = 0
45
45
 
46
46
  begin
47
47
  result = UCharPointer.new(length)
48
- Lib.check_error { |status| length = yield result, status }
48
+ Lib.check_error { |status| length = yield(result, status) }
49
49
  rescue BufferOverflowError
50
50
  attempts += 1
51
51
  retry if attempts < 2
52
- raise BufferOverflowError, "needed: #{length}"
52
+ raise(BufferOverflowError, "needed: #{length}")
53
53
  end
54
54
 
55
55
  [result, length]