dommy 0.16.0 → 0.17.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.
- checksums.yaml +4 -4
- data/lib/dommy/animation.rb +1 -0
- data/lib/dommy/backend.rb +3 -0
- data/lib/dommy/blob.rb +6 -0
- data/lib/dommy/browser.rb +12 -4
- data/lib/dommy/character_data.rb +1 -13
- data/lib/dommy/cookie_store.rb +2 -1
- data/lib/dommy/document.rb +67 -30
- data/lib/dommy/element.rb +11 -0
- data/lib/dommy/element_internals.rb +12 -0
- data/lib/dommy/encodings/legacy_multibyte.rb +800 -0
- data/lib/dommy/encodings/multibyte_overrides.rb +76 -0
- data/lib/dommy/encodings/prescan.rb +234 -0
- data/lib/dommy/encodings.rb +121 -57
- data/lib/dommy/event.rb +107 -54
- data/lib/dommy/event_source.rb +1 -0
- data/lib/dommy/fetch.rb +244 -53
- data/lib/dommy/file_reader.rb +1 -0
- data/lib/dommy/form_data.rb +22 -9
- data/lib/dommy/form_entry_list.rb +3 -3
- data/lib/dommy/html_canvas_element.rb +85 -12
- data/lib/dommy/html_elements/base.rb +1 -0
- data/lib/dommy/html_elements/forms/input.rb +3 -0
- data/lib/dommy/html_elements/interactive.rb +56 -48
- data/lib/dommy/html_elements/metadata.rb +23 -0
- data/lib/dommy/interaction/form_submission.rb +24 -42
- data/lib/dommy/internal/accessibility_tree.rb +149 -162
- data/lib/dommy/internal/accessibility_visibility.rb +16 -17
- data/lib/dommy/internal/accessible_name.rb +231 -246
- data/lib/dommy/internal/aria_role.rb +182 -195
- data/lib/dommy/internal/aria_snapshot.rb +35 -38
- data/lib/dommy/internal/aria_state.rb +102 -118
- data/lib/dommy/internal/autofill.rb +75 -75
- data/lib/dommy/internal/backend_prefilter.rb +127 -130
- data/lib/dommy/internal/child_navigable.rb +86 -83
- data/lib/dommy/internal/close_watcher_manager.rb +1 -0
- data/lib/dommy/internal/css/ancestor_filter.rb +64 -64
- data/lib/dommy/internal/css/calc.rb +90 -71
- data/lib/dommy/internal/css/cascade.rb +69 -67
- data/lib/dommy/internal/css/color.rb +249 -187
- data/lib/dommy/internal/css/computed_style_builder.rb +34 -0
- data/lib/dommy/internal/css/computed_style_declaration.rb +50 -11
- data/lib/dommy/internal/css/counters.rb +67 -67
- data/lib/dommy/internal/css/custom_properties.rb +114 -114
- data/lib/dommy/internal/css/parser.rb +277 -239
- data/lib/dommy/internal/css/property_registry.rb +321 -302
- data/lib/dommy/internal/css/renderability.rb +37 -35
- data/lib/dommy/internal/css/rule_index.rb +2 -2
- data/lib/dommy/internal/css/rule_index_inputs.rb +2 -2
- data/lib/dommy/internal/css/supports.rb +115 -113
- data/lib/dommy/internal/css/ua_stylesheet.rb +55 -53
- data/lib/dommy/internal/css_rule_text.rb +121 -119
- data/lib/dommy/internal/css_source.rb +1 -3
- data/lib/dommy/internal/css_syntax.rb +157 -92
- data/lib/dommy/internal/declarative_shadow_roots.rb +1 -0
- data/lib/dommy/internal/directionality.rb +120 -123
- data/lib/dommy/internal/document_live_ranges.rb +26 -1
- data/lib/dommy/internal/dom_matching.rb +185 -184
- data/lib/dommy/internal/element_geometry.rb +8 -1
- data/lib/dommy/internal/element_matching.rb +71 -69
- data/lib/dommy/internal/element_shadow.rb +1 -13
- data/lib/dommy/internal/element_state.rb +255 -265
- data/lib/dommy/internal/event_handlers.rb +66 -64
- data/lib/dommy/internal/exception_report.rb +88 -86
- data/lib/dommy/internal/focusability.rb +205 -205
- data/lib/dommy/internal/global_functions.rb +49 -47
- data/lib/dommy/internal/idl_reflection.rb +99 -97
- data/lib/dommy/internal/js_number.rb +35 -33
- data/lib/dommy/internal/light_dismiss.rb +130 -132
- data/lib/dommy/internal/literal_lookup.rb +75 -75
- data/lib/dommy/internal/mime_type.rb +54 -49
- data/lib/dommy/internal/mutation_coordinator.rb +46 -0
- data/lib/dommy/internal/mutation_observer_microtask.rb +60 -0
- data/lib/dommy/internal/namespaces.rb +51 -49
- data/lib/dommy/internal/node_adopter.rb +25 -1
- data/lib/dommy/internal/node_equality.rb +61 -59
- data/lib/dommy/internal/post_insertion_steps.rb +1 -0
- data/lib/dommy/internal/referrer.rb +144 -0
- data/lib/dommy/internal/rendered_text/collector.rb +22 -3
- data/lib/dommy/internal/rendered_text/computed_box.rb +3 -2
- data/lib/dommy/internal/rendered_text/fragment.rb +37 -38
- data/lib/dommy/internal/rendered_text/items.rb +7 -5
- data/lib/dommy/internal/selector_matcher.rb +361 -359
- data/lib/dommy/internal/selector_parser.rb +74 -62
- data/lib/dommy/internal/sequential_focus_navigation.rb +150 -151
- data/lib/dommy/internal/shadow_root_registry.rb +7 -0
- data/lib/dommy/internal/slot_assignment.rb +117 -0
- data/lib/dommy/internal/slot_signals.rb +125 -0
- data/lib/dommy/internal/unsafe_html.rb +79 -77
- data/lib/dommy/internal/url_parser.rb +5 -29
- data/lib/dommy/internal/web_idl.rb +158 -153
- data/lib/dommy/internal/window_bound.rb +14 -0
- data/lib/dommy/internal/window_constructors.rb +3 -0
- data/lib/dommy/internal/xml_serialization.rb +298 -296
- data/lib/dommy/js/dom_interfaces.rb +13 -1
- data/lib/dommy/js/host_bridge.rb +23 -1
- data/lib/dommy/js/host_callbacks.rb +7 -0
- data/lib/dommy/js/host_runtime.js +625 -61
- data/lib/dommy/js/import_map.rb +1 -1
- data/lib/dommy/js/observable_runtime.js +295 -128
- data/lib/dommy/js/webidl_members.js +1 -1
- data/lib/dommy/js/webidl_signatures.js +35 -6
- data/lib/dommy/media_query_list.rb +1 -0
- data/lib/dommy/message_channel.rb +2 -0
- data/lib/dommy/mutation_observer.rb +25 -29
- data/lib/dommy/navigator.rb +4 -1
- data/lib/dommy/node.rb +4 -0
- data/lib/dommy/notification.rb +1 -0
- data/lib/dommy/promise.rb +12 -0
- data/lib/dommy/range.rb +20 -3
- data/lib/dommy/shadow_root.rb +25 -1
- data/lib/dommy/streams.rb +18 -0
- data/lib/dommy/style_declaration.rb +15 -4
- data/lib/dommy/text_codec.rb +7 -5
- data/lib/dommy/tree_walker.rb +68 -61
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +1 -0
- data/lib/dommy/window.rb +22 -4
- data/lib/dommy/worker.rb +1 -0
- data/lib/dommy/xml_http_request.rb +12 -2
- data/lib/dommy.rb +17 -2
- metadata +11 -3
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "multibyte_overrides"
|
|
4
|
+
|
|
5
|
+
module Dommy
|
|
6
|
+
module Encodings
|
|
7
|
+
# The legacy multi-byte decoders (Shift_JIS, EUC-JP, ISO-2022-JP, Big5,
|
|
8
|
+
# EUC-KR, gb18030 — GBK's decoder is gb18030's), from the Encoding
|
|
9
|
+
# Standard's handlers: which byte a broken sequence gives back to the
|
|
10
|
+
# stream, and so how many U+FFFD it yields and what follows them, are the
|
|
11
|
+
# algorithm's. Only the index lookup is Ruby's: a pointer is turned back
|
|
12
|
+
# into the bytes that encode it, and Ruby's converter for the encoding whose
|
|
13
|
+
# table matches the spec's index decodes them.
|
|
14
|
+
#
|
|
15
|
+
# Spec: https://encoding.spec.whatwg.org/#legacy-multi-byte-chinese-(simplified)-encodings
|
|
16
|
+
class HandlerDecoder < Decoder
|
|
17
|
+
# Run the handler over `bytes`, then over end-of-queue when `flush`.
|
|
18
|
+
# The handler takes the byte (nil for end-of-queue), the queue it may
|
|
19
|
+
# restore bytes to, and the output, and returns :finished at the end.
|
|
20
|
+
#
|
|
21
|
+
# A restored byte is read before the rest of the input. While the
|
|
22
|
+
# handler holds nothing (#idle?), two shortcuts stand in for it, both
|
|
23
|
+
# exact: a run of ASCII bytes is a run of the same code points in every
|
|
24
|
+
# one of these encodings, so it is copied at once; and two bytes that
|
|
25
|
+
# start with a non-ASCII one decode, from the idle state, to what the
|
|
26
|
+
# handler made of them the first time (#pair_output), so a text's
|
|
27
|
+
# characters are looked up rather than run through the handler again.
|
|
28
|
+
def decode(bytes, flush:)
|
|
29
|
+
out = []
|
|
30
|
+
restored = []
|
|
31
|
+
input = bytes.b
|
|
32
|
+
position = 0
|
|
33
|
+
size = input.bytesize
|
|
34
|
+
loop do
|
|
35
|
+
if !restored.empty?
|
|
36
|
+
handle(restored.shift, restored, out)
|
|
37
|
+
elsif position < size
|
|
38
|
+
if idle?
|
|
39
|
+
stop = input.index(NON_ASCII, position) || size
|
|
40
|
+
if stop > position
|
|
41
|
+
out.concat(input.byteslice(position, stop - position).bytes)
|
|
42
|
+
position = stop
|
|
43
|
+
next
|
|
44
|
+
end
|
|
45
|
+
start = position
|
|
46
|
+
position = pairs(input, position, size, out)
|
|
47
|
+
next if position > start
|
|
48
|
+
end
|
|
49
|
+
byte = input.getbyte(position)
|
|
50
|
+
position += 1
|
|
51
|
+
handle(byte, restored, out)
|
|
52
|
+
else
|
|
53
|
+
break unless flush
|
|
54
|
+
break if handle(nil, restored, out) == :finished && restored.empty?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
out
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
NON_ASCII = /[\x80-\xFF]/n
|
|
61
|
+
private_constant :NON_ASCII
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Whether the handler holds no partial sequence, so that the next ASCII
|
|
66
|
+
# byte is that code point. A stateful decoder whose ASCII means
|
|
67
|
+
# something else in some states answers false.
|
|
68
|
+
def idle? = false
|
|
69
|
+
|
|
70
|
+
def ascii?(byte) = byte && byte <= 0x7F
|
|
71
|
+
|
|
72
|
+
# From the idle state at `position`, append the output of each pair of
|
|
73
|
+
# bytes that starts with a non-ASCII one and that the handler, run on
|
|
74
|
+
# them from its idle state, leaves idle again (bytes it restores
|
|
75
|
+
# included) — remembered per class, so a text's characters are looked
|
|
76
|
+
# up rather than run through the handler again. Stops at an ASCII byte,
|
|
77
|
+
# the last byte, or a longer sequence (three or four bytes, left to the
|
|
78
|
+
# handler); not in fatal mode, where an error must raise where it is.
|
|
79
|
+
# Answers the position reached.
|
|
80
|
+
def pairs(input, position, size, out)
|
|
81
|
+
return position if @fatal
|
|
82
|
+
|
|
83
|
+
cache = self.class.pair_outputs
|
|
84
|
+
while position + 1 < size && (lead = input.getbyte(position)) > 0x7F
|
|
85
|
+
key = (lead << 8) | input.getbyte(position + 1)
|
|
86
|
+
output = cache[key]
|
|
87
|
+
output = cache[key] = self.class.new(false).__send__(:run_pair, lead, key & 0xFF) if output.nil?
|
|
88
|
+
break unless output
|
|
89
|
+
|
|
90
|
+
out.concat(output)
|
|
91
|
+
position += 2
|
|
92
|
+
end
|
|
93
|
+
position
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# On a fresh decoder: handle the two bytes and whatever they restore,
|
|
97
|
+
# and answer the output if it ends idle, else false.
|
|
98
|
+
def run_pair(lead, trail)
|
|
99
|
+
out = []
|
|
100
|
+
restored = []
|
|
101
|
+
handle(lead, restored, out)
|
|
102
|
+
handle(trail, restored, out)
|
|
103
|
+
handle(restored.shift, restored, out) until restored.empty?
|
|
104
|
+
idle? ? out.freeze : false
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class << self
|
|
108
|
+
def pair_outputs = (@pair_outputs ||= {})
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# The code point Ruby's `encoding` decodes `bytes` to, or nil when they
|
|
112
|
+
# decode to nothing (or to more than one code point).
|
|
113
|
+
def self.lookup(encoding, bytes)
|
|
114
|
+
text = bytes.pack("C*").force_encoding(encoding).encode(::Encoding::UTF_8)
|
|
115
|
+
text.length == 1 ? text.ord : nil
|
|
116
|
+
rescue ::EncodingError
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# The spec's indexes. Each is read through the Ruby encoding whose table
|
|
122
|
+
# it nearly is, with the entries where the two differ taken from the
|
|
123
|
+
# spec's own index files (MULTIBYTE_INDEX_OVERRIDES, generated by
|
|
124
|
+
# script/build_encoding_tables.rb), and remembered.
|
|
125
|
+
module LegacyIndexes
|
|
126
|
+
CACHES = Hash.new { |h, k| h[k] = {} }
|
|
127
|
+
private_constant :CACHES
|
|
128
|
+
|
|
129
|
+
module_function
|
|
130
|
+
|
|
131
|
+
def jis0208(pointer) = lookup(:jis0208, pointer)
|
|
132
|
+
def jis0212(pointer) = lookup(:jis0212, pointer)
|
|
133
|
+
def big5(pointer) = lookup(:big5, pointer)
|
|
134
|
+
def euc_kr(pointer) = lookup(:euc_kr, pointer)
|
|
135
|
+
def gb18030(pointer) = lookup(:gb18030, pointer)
|
|
136
|
+
|
|
137
|
+
def lookup(name, pointer)
|
|
138
|
+
cache = CACHES[name]
|
|
139
|
+
return cache[pointer] if cache.key?(pointer)
|
|
140
|
+
|
|
141
|
+
overrides = MULTIBYTE_INDEX_OVERRIDES[name]
|
|
142
|
+
cache[pointer] =
|
|
143
|
+
if overrides.key?(pointer) then overrides[pointer]
|
|
144
|
+
elsif MULTIBYTE_INDEX_NULLS[name]&.any? { |range| range.cover?(pointer) } then nil
|
|
145
|
+
else ruby_lookup(name, pointer)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The code point Ruby's table gives the bytes a pointer stands for —
|
|
150
|
+
# what the overrides are measured against.
|
|
151
|
+
def ruby_lookup(name, pointer)
|
|
152
|
+
case name
|
|
153
|
+
when :jis0208
|
|
154
|
+
# JIS X 0208 with the NEC and IBM extensions is Windows-31J: a
|
|
155
|
+
# pointer is its Shift_JIS byte pair.
|
|
156
|
+
leading, trailing = pointer.divmod(188)
|
|
157
|
+
bytes = [leading + (leading < 0x1F ? 0x81 : 0xC1), trailing + (trailing < 0x3F ? 0x40 : 0x41)]
|
|
158
|
+
HandlerDecoder.lookup(::Encoding::Windows_31J, bytes)
|
|
159
|
+
when :jis0212
|
|
160
|
+
leading, trailing = pointer.divmod(94)
|
|
161
|
+
HandlerDecoder.lookup(::Encoding::EUC_JP, [0x8F, leading + 0xA1, trailing + 0xA1])
|
|
162
|
+
when :big5
|
|
163
|
+
leading, trailing = pointer.divmod(157)
|
|
164
|
+
HandlerDecoder.lookup(::Encoding::Big5_HKSCS, [leading + 0x81, trailing + (trailing < 0x3F ? 0x40 : 0x62)])
|
|
165
|
+
when :euc_kr
|
|
166
|
+
leading, trailing = pointer.divmod(190)
|
|
167
|
+
HandlerDecoder.lookup(::Encoding::CP949, [leading + 0x81, trailing + 0x41])
|
|
168
|
+
when :gb18030
|
|
169
|
+
leading, trailing = pointer.divmod(190)
|
|
170
|
+
HandlerDecoder.lookup(::Encoding::GB18030, [leading + 0x81, trailing + (trailing < 0x3F ? 0x40 : 0x41)])
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# How many pointers each index spans (its decoder's lead bytes times
|
|
175
|
+
# trail bytes), for the generator's comparison.
|
|
176
|
+
POINTER_COUNTS = {jis0208: 60 * 188, jis0212: 94 * 94, big5: 126 * 157, euc_kr: 126 * 190, gb18030: 126 * 190}.freeze
|
|
177
|
+
|
|
178
|
+
# The index gb18030 ranges code point for a four-byte pointer.
|
|
179
|
+
def gb18030_ranges(pointer)
|
|
180
|
+
return nil if (pointer > 39_419 && pointer < 189_000) || pointer > 1_237_575
|
|
181
|
+
return 0xE7C7 if pointer == 7457
|
|
182
|
+
|
|
183
|
+
cache = CACHES[:gb18030_ranges]
|
|
184
|
+
return cache[pointer] if cache.key?(pointer)
|
|
185
|
+
|
|
186
|
+
first, rest = pointer.divmod(12_600)
|
|
187
|
+
second, rest = rest.divmod(1260)
|
|
188
|
+
third, fourth = rest.divmod(10)
|
|
189
|
+
cache[pointer] = HandlerDecoder.lookup(::Encoding::GB18030, [first + 0x81, second + 0x30, third + 0x81, fourth + 0x30])
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Spec: https://encoding.spec.whatwg.org/#shift_jis-decoder
|
|
194
|
+
class ShiftJisDecoder < HandlerDecoder
|
|
195
|
+
private def idle? = @leading.zero?
|
|
196
|
+
|
|
197
|
+
def initialize(fatal)
|
|
198
|
+
super
|
|
199
|
+
@leading = 0x00
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
def handle(byte, queue, out)
|
|
205
|
+
if byte.nil?
|
|
206
|
+
return :finished if @leading.zero?
|
|
207
|
+
|
|
208
|
+
@leading = 0x00
|
|
209
|
+
return out << replacement!
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
unless @leading.zero?
|
|
213
|
+
leading = @leading
|
|
214
|
+
@leading = 0x00
|
|
215
|
+
pointer = nil
|
|
216
|
+
offset = byte < 0x7F ? 0x40 : 0x41
|
|
217
|
+
leading_offset = leading < 0xA0 ? 0x81 : 0xC1
|
|
218
|
+
pointer = (leading - leading_offset) * 188 + byte - offset if byte.between?(0x40, 0x7E) || byte.between?(0x80, 0xFC)
|
|
219
|
+
# Windows' end-user-defined characters, as the Private Use Area.
|
|
220
|
+
return out << (0xE000 - 8836 + pointer) if pointer&.between?(8836, 10_715)
|
|
221
|
+
|
|
222
|
+
code_point = pointer && LegacyIndexes.jis0208(pointer)
|
|
223
|
+
return out << code_point if code_point
|
|
224
|
+
|
|
225
|
+
queue.unshift(byte) if ascii?(byte)
|
|
226
|
+
return out << replacement!
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
if ascii?(byte) || byte == 0x80
|
|
230
|
+
out << byte
|
|
231
|
+
elsif byte.between?(0xA1, 0xDF)
|
|
232
|
+
out << (0xFF61 - 0xA1 + byte)
|
|
233
|
+
elsif byte.between?(0x81, 0x9F) || byte.between?(0xE0, 0xFC)
|
|
234
|
+
@leading = byte
|
|
235
|
+
else
|
|
236
|
+
out << replacement!
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Spec: https://encoding.spec.whatwg.org/#euc-jp-decoder
|
|
242
|
+
class EucJpDecoder < HandlerDecoder
|
|
243
|
+
private def idle? = @leading.zero?
|
|
244
|
+
|
|
245
|
+
def initialize(fatal)
|
|
246
|
+
super
|
|
247
|
+
@leading = 0x00
|
|
248
|
+
@jis0212 = false
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
def handle(byte, queue, out)
|
|
254
|
+
if byte.nil?
|
|
255
|
+
return :finished if @leading.zero?
|
|
256
|
+
|
|
257
|
+
@leading = 0x00
|
|
258
|
+
return out << replacement!
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
if @leading == 0x8E && byte.between?(0xA1, 0xDF)
|
|
262
|
+
@leading = 0x00
|
|
263
|
+
return out << (0xFF61 - 0xA1 + byte)
|
|
264
|
+
end
|
|
265
|
+
if @leading == 0x8F && byte.between?(0xA1, 0xFE)
|
|
266
|
+
@jis0212 = true
|
|
267
|
+
return @leading = byte
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
unless @leading.zero?
|
|
271
|
+
leading = @leading
|
|
272
|
+
@leading = 0x00
|
|
273
|
+
code_point = nil
|
|
274
|
+
if leading.between?(0xA1, 0xFE) && byte.between?(0xA1, 0xFE)
|
|
275
|
+
pointer = (leading - 0xA1) * 94 + byte - 0xA1
|
|
276
|
+
code_point = @jis0212 ? LegacyIndexes.jis0212(pointer) : LegacyIndexes.jis0208(pointer)
|
|
277
|
+
end
|
|
278
|
+
@jis0212 = false
|
|
279
|
+
return out << code_point if code_point
|
|
280
|
+
|
|
281
|
+
queue.unshift(byte) if ascii?(byte)
|
|
282
|
+
return out << replacement!
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if ascii?(byte)
|
|
286
|
+
out << byte
|
|
287
|
+
elsif byte == 0x8E || byte == 0x8F || byte.between?(0xA1, 0xFE)
|
|
288
|
+
@leading = byte
|
|
289
|
+
else
|
|
290
|
+
out << replacement!
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Spec: https://encoding.spec.whatwg.org/#iso-2022-jp-decoder
|
|
296
|
+
class Iso2022JpDecoder < HandlerDecoder
|
|
297
|
+
def initialize(fatal)
|
|
298
|
+
super
|
|
299
|
+
@state = :ascii
|
|
300
|
+
@output_state = :ascii
|
|
301
|
+
@leading = 0x00
|
|
302
|
+
@output = false
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
private
|
|
306
|
+
|
|
307
|
+
def handle(byte, queue, out)
|
|
308
|
+
case @state
|
|
309
|
+
when :ascii, :roman, :katakana, :leading_byte
|
|
310
|
+
return @state = :escape_start if byte == 0x1B
|
|
311
|
+
return :finished if byte.nil?
|
|
312
|
+
|
|
313
|
+
@output = false
|
|
314
|
+
single_state(byte, out)
|
|
315
|
+
when :trailing_byte
|
|
316
|
+
if byte == 0x1B
|
|
317
|
+
@state = :escape_start
|
|
318
|
+
return out << replacement!
|
|
319
|
+
end
|
|
320
|
+
@state = :leading_byte
|
|
321
|
+
return out << replacement! unless byte&.between?(0x21, 0x7E)
|
|
322
|
+
|
|
323
|
+
out << (LegacyIndexes.jis0208((@leading - 0x21) * 94 + byte - 0x21) || replacement!)
|
|
324
|
+
when :escape_start
|
|
325
|
+
if byte == 0x24 || byte == 0x28
|
|
326
|
+
@leading = byte
|
|
327
|
+
return @state = :escape
|
|
328
|
+
end
|
|
329
|
+
queue.unshift(byte) unless byte.nil?
|
|
330
|
+
@output = false
|
|
331
|
+
@state = @output_state
|
|
332
|
+
out << replacement!
|
|
333
|
+
when :escape
|
|
334
|
+
escape(byte, queue, out)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def single_state(byte, out)
|
|
339
|
+
case @state
|
|
340
|
+
when :ascii
|
|
341
|
+
out << (byte <= 0x7F && byte != 0x0E && byte != 0x0F ? byte : replacement!)
|
|
342
|
+
when :roman
|
|
343
|
+
out << case byte
|
|
344
|
+
when 0x5C then 0xA5
|
|
345
|
+
when 0x7E then 0x203E
|
|
346
|
+
else byte <= 0x7F && byte != 0x0E && byte != 0x0F ? byte : replacement!
|
|
347
|
+
end
|
|
348
|
+
when :katakana
|
|
349
|
+
out << (byte.between?(0x21, 0x5F) ? 0xFF61 - 0x21 + byte : replacement!)
|
|
350
|
+
when :leading_byte
|
|
351
|
+
return out << replacement! unless byte.between?(0x21, 0x7E)
|
|
352
|
+
|
|
353
|
+
@leading = byte
|
|
354
|
+
@state = :trailing_byte
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def escape(byte, queue, out)
|
|
359
|
+
leading = @leading
|
|
360
|
+
@leading = 0x00
|
|
361
|
+
state =
|
|
362
|
+
if leading == 0x28
|
|
363
|
+
{0x42 => :ascii, 0x4A => :roman, 0x49 => :katakana}[byte]
|
|
364
|
+
elsif leading == 0x24 && (byte == 0x40 || byte == 0x42)
|
|
365
|
+
:leading_byte
|
|
366
|
+
end
|
|
367
|
+
if state
|
|
368
|
+
@state = @output_state = state
|
|
369
|
+
output = @output
|
|
370
|
+
@output = true
|
|
371
|
+
# Two escapes in a row with nothing between them are an error.
|
|
372
|
+
return output ? out << replacement! : nil
|
|
373
|
+
end
|
|
374
|
+
byte.nil? ? queue.unshift(leading) : queue.unshift(leading, byte)
|
|
375
|
+
@output = false
|
|
376
|
+
@state = @output_state
|
|
377
|
+
out << replacement!
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Spec: https://encoding.spec.whatwg.org/#big5-decoder
|
|
382
|
+
class Big5Decoder < HandlerDecoder
|
|
383
|
+
private def idle? = @leading.zero?
|
|
384
|
+
|
|
385
|
+
# Pointers whose index entry is two code points.
|
|
386
|
+
PAIRS = {1133 => [0x00CA, 0x0304], 1135 => [0x00CA, 0x030C], 1164 => [0x00EA, 0x0304], 1166 => [0x00EA, 0x030C]}.freeze
|
|
387
|
+
|
|
388
|
+
def initialize(fatal)
|
|
389
|
+
super
|
|
390
|
+
@leading = 0x00
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
private
|
|
394
|
+
|
|
395
|
+
def handle(byte, queue, out)
|
|
396
|
+
if byte.nil?
|
|
397
|
+
return :finished if @leading.zero?
|
|
398
|
+
|
|
399
|
+
@leading = 0x00
|
|
400
|
+
return out << replacement!
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
unless @leading.zero?
|
|
404
|
+
leading = @leading
|
|
405
|
+
@leading = 0x00
|
|
406
|
+
offset = byte < 0x7F ? 0x40 : 0x62
|
|
407
|
+
pointer = (leading - 0x81) * 157 + (byte - offset) if byte.between?(0x40, 0x7E) || byte.between?(0xA1, 0xFE)
|
|
408
|
+
return out.concat(PAIRS[pointer]) if PAIRS.key?(pointer)
|
|
409
|
+
|
|
410
|
+
code_point = pointer && LegacyIndexes.big5(pointer)
|
|
411
|
+
return out << code_point if code_point
|
|
412
|
+
|
|
413
|
+
queue.unshift(byte) if ascii?(byte)
|
|
414
|
+
return out << replacement!
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
if ascii?(byte)
|
|
418
|
+
out << byte
|
|
419
|
+
elsif byte.between?(0x81, 0xFE)
|
|
420
|
+
@leading = byte
|
|
421
|
+
else
|
|
422
|
+
out << replacement!
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Spec: https://encoding.spec.whatwg.org/#euc-kr-decoder
|
|
428
|
+
class EucKrDecoder < HandlerDecoder
|
|
429
|
+
private def idle? = @leading.zero?
|
|
430
|
+
|
|
431
|
+
def initialize(fatal)
|
|
432
|
+
super
|
|
433
|
+
@leading = 0x00
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
private
|
|
437
|
+
|
|
438
|
+
def handle(byte, queue, out)
|
|
439
|
+
if byte.nil?
|
|
440
|
+
return :finished if @leading.zero?
|
|
441
|
+
|
|
442
|
+
@leading = 0x00
|
|
443
|
+
return out << replacement!
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
unless @leading.zero?
|
|
447
|
+
leading = @leading
|
|
448
|
+
@leading = 0x00
|
|
449
|
+
pointer = (leading - 0x81) * 190 + (byte - 0x41) if byte.between?(0x41, 0xFE)
|
|
450
|
+
code_point = pointer && LegacyIndexes.euc_kr(pointer)
|
|
451
|
+
return out << code_point if code_point
|
|
452
|
+
|
|
453
|
+
queue.unshift(byte) if ascii?(byte)
|
|
454
|
+
return out << replacement!
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
if ascii?(byte)
|
|
458
|
+
out << byte
|
|
459
|
+
elsif byte.between?(0x81, 0xFE)
|
|
460
|
+
@leading = byte
|
|
461
|
+
else
|
|
462
|
+
out << replacement!
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Spec: https://encoding.spec.whatwg.org/#gb18030-decoder
|
|
468
|
+
class Gb18030Decoder < HandlerDecoder
|
|
469
|
+
private def idle? = @first.zero? && @second.zero? && @third.zero?
|
|
470
|
+
|
|
471
|
+
def initialize(fatal)
|
|
472
|
+
super
|
|
473
|
+
@first = @second = @third = 0x00
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
private
|
|
477
|
+
|
|
478
|
+
def handle(byte, queue, out)
|
|
479
|
+
if byte.nil?
|
|
480
|
+
return :finished if @first.zero? && @second.zero? && @third.zero?
|
|
481
|
+
|
|
482
|
+
@first = @second = @third = 0x00
|
|
483
|
+
return out << replacement!
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
unless @third.zero?
|
|
487
|
+
unless byte.between?(0x30, 0x39)
|
|
488
|
+
queue.unshift(@second, @third, byte)
|
|
489
|
+
@first = @second = @third = 0x00
|
|
490
|
+
return out << replacement!
|
|
491
|
+
end
|
|
492
|
+
pointer = ((@first - 0x81) * 12_600) + ((@second - 0x30) * 1260) + ((@third - 0x81) * 10) + byte - 0x30
|
|
493
|
+
@first = @second = @third = 0x00
|
|
494
|
+
return out << (LegacyIndexes.gb18030_ranges(pointer) || replacement!)
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
unless @second.zero?
|
|
498
|
+
return @third = byte if byte.between?(0x81, 0xFE)
|
|
499
|
+
|
|
500
|
+
queue.unshift(@second, byte)
|
|
501
|
+
@first = @second = 0x00
|
|
502
|
+
return out << replacement!
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
unless @first.zero?
|
|
506
|
+
return @second = byte if byte.between?(0x30, 0x39)
|
|
507
|
+
|
|
508
|
+
leading = @first
|
|
509
|
+
@first = 0x00
|
|
510
|
+
offset = byte < 0x7F ? 0x40 : 0x41
|
|
511
|
+
pointer = (leading - 0x81) * 190 + (byte - offset) if byte.between?(0x40, 0x7E) || byte.between?(0x80, 0xFE)
|
|
512
|
+
code_point = pointer && LegacyIndexes.gb18030(pointer)
|
|
513
|
+
return out << code_point if code_point
|
|
514
|
+
|
|
515
|
+
queue.unshift(byte) if ascii?(byte)
|
|
516
|
+
return out << replacement!
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
if ascii?(byte)
|
|
520
|
+
out << byte
|
|
521
|
+
elsif byte == 0x80
|
|
522
|
+
out << 0x20AC
|
|
523
|
+
elsif byte.between?(0x81, 0xFE)
|
|
524
|
+
@first = byte
|
|
525
|
+
else
|
|
526
|
+
out << replacement!
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
module Dommy
|
|
534
|
+
module Encodings
|
|
535
|
+
# The Encoding Standard's Big5 and ISO-2022-JP encoders, which choose
|
|
536
|
+
# pointers (and, for ISO-2022-JP, escape sequences) as Ruby's converters
|
|
537
|
+
# do not. `encode` returns the bytes, with each code point the encoding
|
|
538
|
+
# cannot represent yielded to the block, which answers the bytes to put
|
|
539
|
+
# in its place (the URL parser's percent-encoded `&#N;`).
|
|
540
|
+
#
|
|
541
|
+
# Spec: https://encoding.spec.whatwg.org/#big5-encoder
|
|
542
|
+
module LegacyEncoders
|
|
543
|
+
module_function
|
|
544
|
+
|
|
545
|
+
ENCODERS = {"Big5" => :big5, "ISO-2022-JP" => :iso_2022_jp, "Shift_JIS" => :shift_jis,
|
|
546
|
+
"EUC-JP" => :euc_jp, "EUC-KR" => :euc_kr, "GBK" => :gbk, "gb18030" => :gb18030}.freeze
|
|
547
|
+
private_constant :ENCODERS
|
|
548
|
+
|
|
549
|
+
def handles?(name) = ENCODERS.key?(name)
|
|
550
|
+
|
|
551
|
+
# `string` encoded in `name` as bytes; a code point the encoding cannot
|
|
552
|
+
# represent is the block's bytes for it.
|
|
553
|
+
def encode(name, string, &on_error)
|
|
554
|
+
__send__(ENCODERS.fetch(name), string, &on_error)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Spec: https://encoding.spec.whatwg.org/#shift_jis-encoder
|
|
558
|
+
def shift_jis(string)
|
|
559
|
+
out = []
|
|
560
|
+
string.each_codepoint do |code_point|
|
|
561
|
+
next out << code_point if code_point <= 0x80
|
|
562
|
+
next out << 0x5C if code_point == 0xA5
|
|
563
|
+
next out << 0x7E if code_point == 0x203E
|
|
564
|
+
next out << code_point - 0xFF61 + 0xA1 if code_point.between?(0xFF61, 0xFF9F)
|
|
565
|
+
|
|
566
|
+
pointer = shift_jis_pointer(code_point == 0x2212 ? 0xFF0D : code_point)
|
|
567
|
+
next out.concat(yield(code_point)) if pointer.nil?
|
|
568
|
+
|
|
569
|
+
leading, trailing = pointer.divmod(188)
|
|
570
|
+
out << leading + (leading < 0x1F ? 0x81 : 0xC1) << trailing + (trailing < 0x3F ? 0x40 : 0x41)
|
|
571
|
+
end
|
|
572
|
+
out
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Spec: https://encoding.spec.whatwg.org/#euc-jp-encoder
|
|
576
|
+
def euc_jp(string)
|
|
577
|
+
out = []
|
|
578
|
+
string.each_codepoint do |code_point|
|
|
579
|
+
next out << code_point if code_point <= 0x7F
|
|
580
|
+
next out << 0x5C if code_point == 0xA5
|
|
581
|
+
next out << 0x7E if code_point == 0x203E
|
|
582
|
+
next out << 0x8E << code_point - 0xFF61 + 0xA1 if code_point.between?(0xFF61, 0xFF9F)
|
|
583
|
+
|
|
584
|
+
pointer = jis0208_first_pointer(code_point == 0x2212 ? 0xFF0D : code_point)
|
|
585
|
+
next out.concat(yield(code_point)) if pointer.nil?
|
|
586
|
+
|
|
587
|
+
leading, trailing = pointer.divmod(94)
|
|
588
|
+
out << leading + 0xA1 << trailing + 0xA1
|
|
589
|
+
end
|
|
590
|
+
out
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# Spec: https://encoding.spec.whatwg.org/#euc-kr-encoder
|
|
594
|
+
def euc_kr(string)
|
|
595
|
+
out = []
|
|
596
|
+
string.each_codepoint do |code_point|
|
|
597
|
+
next out << code_point if code_point <= 0x7F
|
|
598
|
+
|
|
599
|
+
pointer = euc_kr_pointer(code_point)
|
|
600
|
+
next out.concat(yield(code_point)) if pointer.nil?
|
|
601
|
+
|
|
602
|
+
leading, trailing = pointer.divmod(190)
|
|
603
|
+
out << leading + 0x81 << trailing + 0x41
|
|
604
|
+
end
|
|
605
|
+
out
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def gbk(string, &) = gb18030(string, gbk: true, &)
|
|
609
|
+
|
|
610
|
+
# The private-use code points gb18030 encodes as two bytes ahead of the
|
|
611
|
+
# index: what the index had for those pointers before GB18030-2022.
|
|
612
|
+
GB18030_PRIVATE_USE_PAIRS = {
|
|
613
|
+
0xE78D => [0xA6, 0xD9], 0xE78E => [0xA6, 0xDA], 0xE78F => [0xA6, 0xDB], 0xE790 => [0xA6, 0xDC],
|
|
614
|
+
0xE791 => [0xA6, 0xDD], 0xE792 => [0xA6, 0xDE], 0xE793 => [0xA6, 0xDF], 0xE794 => [0xA6, 0xEC],
|
|
615
|
+
0xE795 => [0xA6, 0xED], 0xE796 => [0xA6, 0xF3], 0xE81E => [0xFE, 0x59], 0xE826 => [0xFE, 0x61],
|
|
616
|
+
0xE82B => [0xFE, 0x66], 0xE82C => [0xFE, 0x67], 0xE832 => [0xFE, 0x6D], 0xE843 => [0xFE, 0x7E],
|
|
617
|
+
0xE854 => [0xFE, 0x90], 0xE864 => [0xFE, 0xA0]
|
|
618
|
+
}.freeze
|
|
619
|
+
private_constant :GB18030_PRIVATE_USE_PAIRS
|
|
620
|
+
|
|
621
|
+
# Spec: https://encoding.spec.whatwg.org/#gb18030-encoder
|
|
622
|
+
def gb18030(string, gbk: false)
|
|
623
|
+
out = []
|
|
624
|
+
string.each_codepoint do |code_point|
|
|
625
|
+
next out << code_point if code_point <= 0x7F
|
|
626
|
+
next out.concat(yield(code_point)) if code_point == 0xE5E5
|
|
627
|
+
next out << 0x80 if gbk && code_point == 0x20AC
|
|
628
|
+
next out.concat(GB18030_PRIVATE_USE_PAIRS[code_point]) if GB18030_PRIVATE_USE_PAIRS.key?(code_point)
|
|
629
|
+
|
|
630
|
+
if (pointer = gb18030_pointer(code_point))
|
|
631
|
+
leading, trailing = pointer.divmod(190)
|
|
632
|
+
next out << leading + 0x81 << trailing + (trailing < 0x3F ? 0x40 : 0x41)
|
|
633
|
+
end
|
|
634
|
+
bytes = gbk ? nil : gb18030_four_bytes(code_point)
|
|
635
|
+
next out.concat(yield(code_point)) if bytes.nil?
|
|
636
|
+
|
|
637
|
+
out.concat(bytes)
|
|
638
|
+
end
|
|
639
|
+
out
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
# The index gb18030 pointer: the first pointer for a code point.
|
|
643
|
+
def gb18030_pointer(code_point)
|
|
644
|
+
(@gb18030_pointers ||= first_pointers(:gb18030, 0...LegacyIndexes::POINTER_COUNTS[:gb18030]))[code_point]
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
# The four bytes of a code point's index gb18030 ranges pointer. The
|
|
648
|
+
# ranges are Ruby's GB18030's four-byte sequences (the decoder reads
|
|
649
|
+
# them back the same way), but for U+E7C7, whose pointer is 7457.
|
|
650
|
+
def gb18030_four_bytes(code_point)
|
|
651
|
+
return [0x81, 0x35, 0xF4, 0x37] if code_point == 0xE7C7
|
|
652
|
+
|
|
653
|
+
bytes = code_point.chr(::Encoding::UTF_8).encode(::Encoding::GB18030).bytes
|
|
654
|
+
bytes if bytes.length == 4
|
|
655
|
+
rescue ::EncodingError
|
|
656
|
+
nil
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def big5(string)
|
|
660
|
+
out = []
|
|
661
|
+
string.each_char do |char|
|
|
662
|
+
code_point = char.ord
|
|
663
|
+
next out << code_point if code_point <= 0x7F
|
|
664
|
+
|
|
665
|
+
pointer = big5_pointer(code_point)
|
|
666
|
+
next out.concat(yield(code_point)) if pointer.nil?
|
|
667
|
+
|
|
668
|
+
leading, trailing = pointer.divmod(157)
|
|
669
|
+
out << leading + 0x81 << trailing + (trailing < 0x3F ? 0x40 : 0x62)
|
|
670
|
+
end
|
|
671
|
+
out
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
# Spec: https://encoding.spec.whatwg.org/#iso-2022-jp-encoder
|
|
675
|
+
def iso_2022_jp(string)
|
|
676
|
+
out = []
|
|
677
|
+
state = :ascii
|
|
678
|
+
queue = string.codepoints
|
|
679
|
+
until queue.empty?
|
|
680
|
+
code_point = queue.shift
|
|
681
|
+
if [0x0E, 0x0F, 0x1B].include?(code_point) && state != :jis0208
|
|
682
|
+
out.concat(yield(0xFFFD))
|
|
683
|
+
next
|
|
684
|
+
end
|
|
685
|
+
if state == :ascii && code_point <= 0x7F
|
|
686
|
+
out << code_point
|
|
687
|
+
next
|
|
688
|
+
end
|
|
689
|
+
if state == :roman && ((code_point <= 0x7F && code_point != 0x5C && code_point != 0x7E) || code_point == 0xA5 || code_point == 0x203E)
|
|
690
|
+
out << (code_point == 0xA5 ? 0x5C : (code_point == 0x203E ? 0x7E : code_point))
|
|
691
|
+
next
|
|
692
|
+
end
|
|
693
|
+
if code_point <= 0x7F
|
|
694
|
+
queue.unshift(code_point)
|
|
695
|
+
state = :ascii
|
|
696
|
+
out << 0x1B << 0x28 << 0x42
|
|
697
|
+
next
|
|
698
|
+
end
|
|
699
|
+
if code_point == 0xA5 || code_point == 0x203E
|
|
700
|
+
queue.unshift(code_point)
|
|
701
|
+
state = :roman
|
|
702
|
+
out << 0x1B << 0x28 << 0x4A
|
|
703
|
+
next
|
|
704
|
+
end
|
|
705
|
+
original = code_point
|
|
706
|
+
code_point = 0xFF0D if code_point == 0x2212
|
|
707
|
+
code_point = katakana(code_point) if code_point.between?(0xFF61, 0xFF9F)
|
|
708
|
+
pointer = jis0208_pointer(code_point)
|
|
709
|
+
if pointer.nil?
|
|
710
|
+
if state == :jis0208
|
|
711
|
+
queue.unshift(original)
|
|
712
|
+
state = :ascii
|
|
713
|
+
out << 0x1B << 0x28 << 0x42
|
|
714
|
+
else
|
|
715
|
+
out.concat(yield(original))
|
|
716
|
+
end
|
|
717
|
+
next
|
|
718
|
+
end
|
|
719
|
+
if state != :jis0208
|
|
720
|
+
queue.unshift(original)
|
|
721
|
+
state = :jis0208
|
|
722
|
+
out << 0x1B << 0x24 << 0x42
|
|
723
|
+
next
|
|
724
|
+
end
|
|
725
|
+
leading, trailing = pointer.divmod(94)
|
|
726
|
+
out << leading + 0x21 << trailing + 0x21
|
|
727
|
+
end
|
|
728
|
+
out << 0x1B << 0x28 << 0x42 unless state == :ascii
|
|
729
|
+
out
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
# index ISO-2022-JP katakana: halfwidth to fullwidth katakana, as NFKC
|
|
733
|
+
# maps them, but with the voiced sound marks as their spacing forms.
|
|
734
|
+
def katakana(code_point)
|
|
735
|
+
return 0x309B if code_point == 0xFF9E
|
|
736
|
+
return 0x309C if code_point == 0xFF9F
|
|
737
|
+
|
|
738
|
+
code_point.chr(::Encoding::UTF_8).unicode_normalize(:nfkc).ord
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
BIG5_LAST_POINTER = [0x2550, 0x255E, 0x2561, 0x256A, 0x5341, 0x5345].freeze
|
|
742
|
+
private_constant :BIG5_LAST_POINTER
|
|
743
|
+
|
|
744
|
+
# The index Big5 pointer: Big5 without the HKSCS extensions below 0xA1,
|
|
745
|
+
# the first pointer for a code point, but the last for six duplicates.
|
|
746
|
+
def big5_pointer(code_point)
|
|
747
|
+
@big5_pointers ||= begin
|
|
748
|
+
first = {}
|
|
749
|
+
last = {}
|
|
750
|
+
((0xA1 - 0x81) * 157...((0xFE - 0x81 + 1) * 157)).each do |pointer|
|
|
751
|
+
mapped = LegacyIndexes.big5(pointer)
|
|
752
|
+
next unless mapped
|
|
753
|
+
|
|
754
|
+
first[mapped] ||= pointer
|
|
755
|
+
last[mapped] = pointer
|
|
756
|
+
end
|
|
757
|
+
BIG5_LAST_POINTER.each { |cp| first[cp] = last[cp] if last[cp] }
|
|
758
|
+
first
|
|
759
|
+
end
|
|
760
|
+
@big5_pointers[code_point]
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
# The first pointer for each code point of `index` over `pointers`.
|
|
764
|
+
def first_pointers(index, pointers)
|
|
765
|
+
pointers.each_with_object({}) do |pointer, map|
|
|
766
|
+
mapped = LegacyIndexes.public_send(index, pointer)
|
|
767
|
+
map[mapped] ||= pointer if mapped
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
# The index jis0208 pointer over the whole index, as EUC-JP's encoder
|
|
772
|
+
# asks for it.
|
|
773
|
+
def jis0208_first_pointer(code_point)
|
|
774
|
+
(@jis0208_first_pointers ||= first_pointers(:jis0208, 0...LegacyIndexes::POINTER_COUNTS[:jis0208]))[code_point]
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
# The index Shift_JIS pointer: index jis0208 without its pointers 8272
|
|
778
|
+
# to 8835 (the NEC selection of IBM extensions, which the IBM rows
|
|
779
|
+
# repeat), so those characters encode as their IBM bytes.
|
|
780
|
+
def shift_jis_pointer(code_point)
|
|
781
|
+
@shift_jis_pointers ||= first_pointers(:jis0208, (0...LegacyIndexes::POINTER_COUNTS[:jis0208]).reject { |pointer| pointer.between?(8272, 8835) })
|
|
782
|
+
@shift_jis_pointers[code_point]
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
# The index EUC-KR pointer: the first pointer for a code point.
|
|
786
|
+
def euc_kr_pointer(code_point)
|
|
787
|
+
(@euc_kr_pointers ||= first_pointers(:euc_kr, 0...LegacyIndexes::POINTER_COUNTS[:euc_kr]))[code_point]
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# The index jis0208 pointer: the first pointer for a code point.
|
|
791
|
+
def jis0208_pointer(code_point)
|
|
792
|
+
@jis0208_pointers ||= (0...(94 * 94)).each_with_object({}) do |pointer, map|
|
|
793
|
+
mapped = LegacyIndexes.jis0208(pointer)
|
|
794
|
+
map[mapped] ||= pointer if mapped
|
|
795
|
+
end
|
|
796
|
+
@jis0208_pointers[code_point]
|
|
797
|
+
end
|
|
798
|
+
end
|
|
799
|
+
end
|
|
800
|
+
end
|