Skip to main content

qrcode_rs/
optimize.rs

1#![allow(clippy::unicode_not_nfc)]
2//! Data mode segmentation optimizer.
3//!
4//! QR codes support four data modes (Numeric, Alphanumeric, Byte, Kanji),
5//! each with different efficiency for different character types. This module
6//! finds the optimal sequence of mode switches to minimize the total number
7//! of bits required to encode the input data.
8//!
9//! The optimizer uses dynamic programming to explore all possible mode
10//! transitions and selects the segmentation that produces the shortest
11//! bit stream for the target QR code version.
12use crate::types::{Mode, Version};
13use std::slice::Iter;
14
15//------------------------------------------------------------------------------
16//{{{ Segment
17
18/// A segment of data committed to an encoding mode.
19#[derive(PartialEq, Eq, Debug, Copy, Clone)]
20pub struct Segment {
21    /// The encoding mode of the segment of data.
22    pub mode: Mode,
23
24    /// The start index of the segment.
25    pub begin: usize,
26
27    /// The end index (exclusive) of the segment.
28    pub end: usize,
29}
30
31impl Segment {
32    /// Compute the number of bits (including the size of the mode indicator and
33    /// length bits) when this segment is encoded.
34    pub fn encoded_len(&self, version: Version) -> usize {
35        let byte_size = self.end - self.begin;
36        let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size };
37
38        let mode_bits_count = version.mode_bits_count();
39        let length_bits_count = self.mode.length_bits_count(version);
40        let data_bits_count = self.mode.data_bits_count(chars_count);
41
42        mode_bits_count + length_bits_count + data_bits_count
43    }
44}
45
46//}}}
47//------------------------------------------------------------------------------
48//{{{ Parser
49
50/// This iterator is basically equivalent to
51///
52/// ```ignore
53/// data.map(|c| ExclCharSet::from_u8(*c))
54///     .chain(Some(ExclCharSet::End).move_iter())
55///     .enumerate()
56/// ```
57///
58/// But the type is too hard to write, thus the new type.
59///
60struct EcsIter<I> {
61    base: I,
62    index: usize,
63    ended: bool,
64}
65
66impl<'a, I: Iterator<Item = &'a u8>> Iterator for EcsIter<I> {
67    type Item = (usize, ExclCharSet);
68
69    fn next(&mut self) -> Option<(usize, ExclCharSet)> {
70        if self.ended {
71            return None;
72        }
73
74        match self.base.next() {
75            None => {
76                self.ended = true;
77                Some((self.index, ExclCharSet::End))
78            }
79            Some(c) => {
80                let old_index = self.index;
81                self.index += 1;
82                Some((old_index, ExclCharSet::from_u8(*c)))
83            }
84        }
85    }
86}
87
88/// QR code data parser to classify the input into distinct segments.
89pub struct Parser<'a> {
90    ecs_iter: EcsIter<Iter<'a, u8>>,
91    state: State,
92    begin: usize,
93    pending_single_byte: bool,
94}
95
96impl<'a> Parser<'a> {
97    /// Creates a new iterator which parse the data into segments that only
98    /// contains their exclusive subsets. No optimization is done at this point.
99    ///
100    ///     use qrcode_rs::optimize::{Parser, Segment};
101    ///     use qrcode_rs::types::Mode::{Alphanumeric, Numeric, Byte};
102    ///
103    ///     let parse_res = Parser::new(b"ABC123abcd").collect::<Vec<Segment>>();
104    ///     assert_eq!(parse_res, vec![Segment { mode: Alphanumeric, begin: 0, end: 3 },
105    ///                                Segment { mode: Numeric, begin: 3, end: 6 },
106    ///                                Segment { mode: Byte, begin: 6, end: 10 }]);
107    ///
108    pub fn new(data: &[u8]) -> Parser<'_> {
109        Parser {
110            ecs_iter: EcsIter { base: data.iter(), index: 0, ended: false },
111            state: State::Init,
112            begin: 0,
113            pending_single_byte: false,
114        }
115    }
116}
117
118impl<'a> Iterator for Parser<'a> {
119    type Item = Segment;
120
121    fn next(&mut self) -> Option<Segment> {
122        if self.pending_single_byte {
123            self.pending_single_byte = false;
124            self.begin += 1;
125            return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin });
126        }
127
128        loop {
129            let (i, ecs) = self.ecs_iter.next()?;
130            let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
131            self.state = next_state;
132
133            let old_begin = self.begin;
134            let push_mode = match action {
135                Action::Idle => continue,
136                Action::Numeric => Mode::Numeric,
137                Action::Alpha => Mode::Alphanumeric,
138                Action::Byte => Mode::Byte,
139                Action::Kanji => Mode::Kanji,
140                Action::KanjiAndSingleByte => {
141                    let next_begin = i - 1;
142                    if self.begin == next_begin {
143                        Mode::Byte
144                    } else {
145                        self.pending_single_byte = true;
146                        self.begin = next_begin;
147                        return Some(Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin });
148                    }
149                }
150            };
151
152            self.begin = i;
153            return Some(Segment { mode: push_mode, begin: old_begin, end: i });
154        }
155    }
156}
157
158#[cfg(test)]
159mod parse_tests {
160    use crate::optimize::{Parser, Segment};
161    use crate::types::Mode;
162
163    fn parse(data: &[u8]) -> Vec<Segment> {
164        Parser::new(data).collect()
165    }
166
167    #[test]
168    fn test_parse_1() {
169        let segs = parse(b"01049123451234591597033130128%10ABC123");
170        assert_eq!(
171            segs,
172            vec![
173                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
174                Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
175                Segment { mode: Mode::Numeric, begin: 30, end: 32 },
176                Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
177                Segment { mode: Mode::Numeric, begin: 35, end: 38 },
178            ]
179        );
180    }
181
182    #[test]
183    fn test_parse_shift_jis_example_1() {
184        let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
185        assert_eq!(
186            segs,
187            vec![
188                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
189                Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
190                Segment { mode: Mode::Byte, begin: 5, end: 6 },
191                Segment { mode: Mode::Kanji, begin: 6, end: 8 },
192            ]
193        );
194    }
195
196    #[test]
197    fn test_parse_utf_8() {
198        // Mojibake?
199        let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
200        assert_eq!(
201            segs,
202            vec![
203                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
204                Segment { mode: Mode::Byte, begin: 4, end: 5 },
205                Segment { mode: Mode::Kanji, begin: 5, end: 7 },
206                Segment { mode: Mode::Byte, begin: 7, end: 10 },
207                Segment { mode: Mode::Kanji, begin: 10, end: 12 },
208                Segment { mode: Mode::Byte, begin: 12, end: 13 },
209            ]
210        );
211    }
212
213    #[test]
214    fn test_not_kanji_1() {
215        let segs = parse(b"\x81\x30");
216        assert_eq!(
217            segs,
218            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Numeric, begin: 1, end: 2 }]
219        );
220    }
221
222    #[test]
223    fn test_not_kanji_2() {
224        // Note that it's implementation detail that the byte seq is split into
225        // two. Perhaps adjust the test to check for this.
226        let segs = parse(b"\xeb\xc0");
227        assert_eq!(
228            segs,
229            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
230        );
231    }
232
233    #[test]
234    fn test_not_kanji_3() {
235        let segs = parse(b"\x81\x7f");
236        assert_eq!(
237            segs,
238            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
239        );
240    }
241
242    #[test]
243    fn test_not_kanji_4() {
244        let segs = parse(b"\x81\x40\x81");
245        assert_eq!(
246            segs,
247            vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 }, Segment { mode: Mode::Byte, begin: 2, end: 3 }]
248        );
249    }
250}
251
252//}}}
253//------------------------------------------------------------------------------
254//{{{ Optimizer
255
256pub struct Optimizer<I> {
257    parser: I,
258    last_segment: Segment,
259    last_segment_size: usize,
260    version: Version,
261    ended: bool,
262}
263
264impl<I: Iterator<Item = Segment>> Optimizer<I> {
265    /// Optimize the segments by combining adjacent segments when possible.
266    ///
267    /// Currently this method uses a greedy algorithm by combining segments from
268    /// left to right until the new segment is longer than before. This method
269    /// does *not* use Annex J from the ISO standard.
270    ///
271    pub fn new(mut segments: I, version: Version) -> Self {
272        match segments.next() {
273            None => Self {
274                parser: segments,
275                last_segment: Segment { mode: Mode::Numeric, begin: 0, end: 0 },
276                last_segment_size: 0,
277                version,
278                ended: true,
279            },
280            Some(segment) => Self {
281                parser: segments,
282                last_segment: segment,
283                last_segment_size: segment.encoded_len(version),
284                version,
285                ended: false,
286            },
287        }
288    }
289}
290
291impl<'a> Parser<'a> {
292    pub fn optimize(self, version: Version) -> Optimizer<Parser<'a>> {
293        Optimizer::new(self, version)
294    }
295}
296
297impl<I: Iterator<Item = Segment>> Iterator for Optimizer<I> {
298    type Item = Segment;
299
300    fn next(&mut self) -> Option<Segment> {
301        if self.ended {
302            return None;
303        }
304
305        loop {
306            match self.parser.next() {
307                None => {
308                    self.ended = true;
309                    return Some(self.last_segment);
310                }
311                Some(segment) => {
312                    let seg_size = segment.encoded_len(self.version);
313
314                    let new_segment = Segment {
315                        mode: self.last_segment.mode.max(segment.mode),
316                        begin: self.last_segment.begin,
317                        end: segment.end,
318                    };
319                    let new_size = new_segment.encoded_len(self.version);
320
321                    if self.last_segment_size + seg_size >= new_size {
322                        self.last_segment = new_segment;
323                        self.last_segment_size = new_size;
324                    } else {
325                        let old_segment = self.last_segment;
326                        self.last_segment = segment;
327                        self.last_segment_size = seg_size;
328                        return Some(old_segment);
329                    }
330                }
331            }
332        }
333    }
334}
335
336/// Computes the total encoded length of all segments.
337pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
338    segments.iter().map(|seg| seg.encoded_len(version)).sum()
339}
340
341#[cfg(test)]
342mod optimize_tests {
343    use crate::optimize::{Optimizer, Segment, total_encoded_len};
344    use crate::types::{Mode, Version};
345
346    fn test_optimization_result(given: &[Segment], expected: &[Segment], version: Version) {
347        let prev_len = total_encoded_len(given, version);
348        let opt_segs = Optimizer::new(given.iter().copied(), version).collect::<Vec<_>>();
349        let new_len = total_encoded_len(&opt_segs, version);
350        if given != opt_segs {
351            assert!(prev_len > new_len, "{prev_len} > {new_len}");
352        }
353        assert_eq!(
354            opt_segs,
355            expected,
356            "Optimization gave something better: {} < {} ({:?})",
357            new_len,
358            total_encoded_len(expected, version),
359            opt_segs
360        );
361    }
362
363    #[test]
364    fn test_example_1() {
365        test_optimization_result(
366            &[
367                Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
368                Segment { mode: Mode::Numeric, begin: 3, end: 6 },
369                Segment { mode: Mode::Byte, begin: 6, end: 10 },
370            ],
371            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 }, Segment { mode: Mode::Byte, begin: 6, end: 10 }],
372            Version::Normal(1),
373        );
374    }
375
376    #[test]
377    fn test_example_2() {
378        test_optimization_result(
379            &[
380                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
381                Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
382                Segment { mode: Mode::Numeric, begin: 30, end: 32 },
383                Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
384                Segment { mode: Mode::Numeric, begin: 35, end: 38 },
385            ],
386            &[
387                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
388                Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 },
389            ],
390            Version::Normal(9),
391        );
392    }
393
394    #[test]
395    fn test_example_3() {
396        test_optimization_result(
397            &[
398                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
399                Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
400                Segment { mode: Mode::Byte, begin: 5, end: 6 },
401                Segment { mode: Mode::Kanji, begin: 6, end: 8 },
402            ],
403            &[Segment { mode: Mode::Byte, begin: 0, end: 8 }],
404            Version::Normal(1),
405        );
406    }
407
408    #[test]
409    fn test_example_4() {
410        test_optimization_result(
411            &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
412            &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
413            Version::Normal(1),
414        );
415    }
416
417    #[test]
418    fn test_annex_j_guideline_1a() {
419        test_optimization_result(
420            &[
421                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
422                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
423            ],
424            &[
425                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
426                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
427            ],
428            Version::Micro(2),
429        );
430    }
431
432    #[test]
433    fn test_annex_j_guideline_1b() {
434        test_optimization_result(
435            &[
436                Segment { mode: Mode::Numeric, begin: 0, end: 2 },
437                Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 },
438            ],
439            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
440            Version::Micro(2),
441        );
442    }
443
444    #[test]
445    fn test_annex_j_guideline_1c() {
446        test_optimization_result(
447            &[
448                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
449                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
450            ],
451            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
452            Version::Micro(3),
453        );
454    }
455}
456
457//}}}
458//------------------------------------------------------------------------------
459//{{{ Internal types and data for parsing
460
461/// All values of `u8` can be split into 9 different character sets when
462/// determining which encoding to use. This enum represents these groupings for
463/// parsing purpose.
464#[derive(Copy, Clone)]
465enum ExclCharSet {
466    /// The end of string.
467    End = 0,
468
469    /// All symbols supported by the Alphanumeric encoding, i.e. space, `$`, `%`,
470    /// `*`, `+`, `-`, `.`, `/` and `:`.
471    Symbol = 1,
472
473    /// All numbers (0–9).
474    Numeric = 2,
475
476    /// All uppercase letters (A–Z). These characters may also appear in the
477    /// second byte of a Shift JIS 2-byte encoding.
478    Alpha = 3,
479
480    /// The first byte of a Shift JIS 2-byte encoding, in the range 0x81–0x9f.
481    KanjiHi1 = 4,
482
483    /// The first byte of a Shift JIS 2-byte encoding, in the range 0xe0–0xea.
484    KanjiHi2 = 5,
485
486    /// The first byte of a Shift JIS 2-byte encoding, of value 0xeb. This is
487    /// different from the other two range that the second byte has a smaller
488    /// range.
489    KanjiHi3 = 6,
490
491    /// The second byte of a Shift JIS 2-byte encoding, in the range 0x40–0xbf,
492    /// excluding letters (covered by `Alpha`), 0x81–0x9f (covered by `KanjiHi1`),
493    /// and the invalid byte 0x7f.
494    KanjiLo1 = 7,
495
496    /// The second byte of a Shift JIS 2-byte encoding, in the range 0xc0–0xfc,
497    /// excluding the range 0xe0–0xeb (covered by `KanjiHi2` and `KanjiHi3`).
498    /// This half of byte-pair cannot appear as the second byte leaded by
499    /// `KanjiHi3`.
500    KanjiLo2 = 8,
501
502    /// Any other values not covered by the above character sets.
503    Byte = 9,
504}
505
506impl ExclCharSet {
507    /// Determines which character set a byte is in.
508    fn from_u8(c: u8) -> Self {
509        match c {
510            0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d..=0x2f | 0x3a => ExclCharSet::Symbol,
511            0x30..=0x39 => ExclCharSet::Numeric,
512            0x41..=0x5a => ExclCharSet::Alpha,
513            0x81..=0x9f => ExclCharSet::KanjiHi1,
514            0xe0..=0xea => ExclCharSet::KanjiHi2,
515            0xeb => ExclCharSet::KanjiHi3,
516            0x40 | 0x5b..=0x7e | 0x80 | 0xa0..=0xbf => ExclCharSet::KanjiLo1,
517            0xc0..=0xdf | 0xec..=0xfc => ExclCharSet::KanjiLo2,
518            _ => ExclCharSet::Byte,
519        }
520    }
521}
522
523/// The current parsing state.
524#[derive(Copy, Clone)]
525enum State {
526    /// Just initialized.
527    Init = 0,
528
529    /// Inside a string that can be exclusively encoded as Numeric.
530    Numeric = 10,
531
532    /// Inside a string that can be exclusively encoded as Alphanumeric.
533    Alpha = 20,
534
535    /// Inside a string that can be exclusively encoded as 8-Bit Byte.
536    Byte = 30,
537
538    /// Just encountered the first byte of a Shift JIS 2-byte sequence of the
539    /// set `KanjiHi1` or `KanjiHi2`.
540    KanjiHi12 = 40,
541
542    /// Just encountered the first byte of a Shift JIS 2-byte sequence of the
543    /// set `KanjiHi3`.
544    KanjiHi3 = 50,
545
546    /// Inside a string that can be exclusively encoded as Kanji.
547    Kanji = 60,
548}
549
550/// What should the parser do after a state transition.
551#[derive(Copy, Clone)]
552enum Action {
553    /// The parser should do nothing.
554    Idle,
555
556    /// Push the current segment as a Numeric string, and reset the marks.
557    Numeric,
558
559    /// Push the current segment as an Alphanumeric string, and reset the marks.
560    Alpha,
561
562    /// Push the current segment as a 8-Bit Byte string, and reset the marks.
563    Byte,
564
565    /// Push the current segment as a Kanji string, and reset the marks.
566    Kanji,
567
568    /// Push the current segment excluding the last byte as a Kanji string, then
569    /// push the remaining single byte as a Byte string, and reset the marks.
570    KanjiAndSingleByte,
571}
572
573static STATE_TRANSITION: [(State, Action); 70] = [
574    // STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
575
576    // Init state:
577    (State::Init, Action::Idle),      // End
578    (State::Alpha, Action::Idle),     // Symbol
579    (State::Numeric, Action::Idle),   // Numeric
580    (State::Alpha, Action::Idle),     // Alpha
581    (State::KanjiHi12, Action::Idle), // KanjiHi1
582    (State::KanjiHi12, Action::Idle), // KanjiHi2
583    (State::KanjiHi3, Action::Idle),  // KanjiHi3
584    (State::Byte, Action::Idle),      // KanjiLo1
585    (State::Byte, Action::Idle),      // KanjiLo2
586    (State::Byte, Action::Idle),      // Byte
587    // Numeric state:
588    (State::Init, Action::Numeric),      // End
589    (State::Alpha, Action::Numeric),     // Symbol
590    (State::Numeric, Action::Idle),      // Numeric
591    (State::Alpha, Action::Numeric),     // Alpha
592    (State::KanjiHi12, Action::Numeric), // KanjiHi1
593    (State::KanjiHi12, Action::Numeric), // KanjiHi2
594    (State::KanjiHi3, Action::Numeric),  // KanjiHi3
595    (State::Byte, Action::Numeric),      // KanjiLo1
596    (State::Byte, Action::Numeric),      // KanjiLo2
597    (State::Byte, Action::Numeric),      // Byte
598    // Alpha state:
599    (State::Init, Action::Alpha),      // End
600    (State::Alpha, Action::Idle),      // Symbol
601    (State::Numeric, Action::Alpha),   // Numeric
602    (State::Alpha, Action::Idle),      // Alpha
603    (State::KanjiHi12, Action::Alpha), // KanjiHi1
604    (State::KanjiHi12, Action::Alpha), // KanjiHi2
605    (State::KanjiHi3, Action::Alpha),  // KanjiHi3
606    (State::Byte, Action::Alpha),      // KanjiLo1
607    (State::Byte, Action::Alpha),      // KanjiLo2
608    (State::Byte, Action::Alpha),      // Byte
609    // Byte state:
610    (State::Init, Action::Byte),      // End
611    (State::Alpha, Action::Byte),     // Symbol
612    (State::Numeric, Action::Byte),   // Numeric
613    (State::Alpha, Action::Byte),     // Alpha
614    (State::KanjiHi12, Action::Byte), // KanjiHi1
615    (State::KanjiHi12, Action::Byte), // KanjiHi2
616    (State::KanjiHi3, Action::Byte),  // KanjiHi3
617    (State::Byte, Action::Idle),      // KanjiLo1
618    (State::Byte, Action::Idle),      // KanjiLo2
619    (State::Byte, Action::Idle),      // Byte
620    // KanjiHi12 state:
621    (State::Init, Action::KanjiAndSingleByte),    // End
622    (State::Alpha, Action::KanjiAndSingleByte),   // Symbol
623    (State::Numeric, Action::KanjiAndSingleByte), // Numeric
624    (State::Kanji, Action::Idle),                 // Alpha
625    (State::Kanji, Action::Idle),                 // KanjiHi1
626    (State::Kanji, Action::Idle),                 // KanjiHi2
627    (State::Kanji, Action::Idle),                 // KanjiHi3
628    (State::Kanji, Action::Idle),                 // KanjiLo1
629    (State::Kanji, Action::Idle),                 // KanjiLo2
630    (State::Byte, Action::KanjiAndSingleByte),    // Byte
631    // KanjiHi3 state:
632    (State::Init, Action::KanjiAndSingleByte),      // End
633    (State::Alpha, Action::KanjiAndSingleByte),     // Symbol
634    (State::Numeric, Action::KanjiAndSingleByte),   // Numeric
635    (State::Kanji, Action::Idle),                   // Alpha
636    (State::Kanji, Action::Idle),                   // KanjiHi1
637    (State::KanjiHi12, Action::KanjiAndSingleByte), // KanjiHi2
638    (State::KanjiHi3, Action::KanjiAndSingleByte),  // KanjiHi3
639    (State::Kanji, Action::Idle),                   // KanjiLo1
640    (State::Byte, Action::KanjiAndSingleByte),      // KanjiLo2
641    (State::Byte, Action::KanjiAndSingleByte),      // Byte
642    // Kanji state:
643    (State::Init, Action::Kanji),     // End
644    (State::Alpha, Action::Kanji),    // Symbol
645    (State::Numeric, Action::Kanji),  // Numeric
646    (State::Alpha, Action::Kanji),    // Alpha
647    (State::KanjiHi12, Action::Idle), // KanjiHi1
648    (State::KanjiHi12, Action::Idle), // KanjiHi2
649    (State::KanjiHi3, Action::Idle),  // KanjiHi3
650    (State::Byte, Action::Kanji),     // KanjiLo1
651    (State::Byte, Action::Kanji),     // KanjiLo2
652    (State::Byte, Action::Kanji),     // Byte
653];
654
655//}}}