1#![allow(clippy::unicode_not_nfc)]
2#[cfg(not(feature = "std"))]
13#[allow(unused_imports)]
14use alloc::{
15 borrow::ToOwned,
16 format,
17 string::{String, ToString},
18 vec,
19 vec::Vec,
20};
21
22use crate::types::{Mode, Version};
23use core::cmp::Reverse;
24use core::marker::PhantomData;
25use core::slice::Iter;
26
27#[derive(PartialEq, Eq, Debug, Copy, Clone)]
32pub struct Segment {
33 pub mode: Mode,
35
36 pub begin: usize,
38
39 pub end: usize,
41}
42
43impl Segment {
44 pub fn encoded_len(&self, version: Version) -> usize {
47 let byte_size = self.end - self.begin;
48 let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size };
49
50 let mode_bits_count = version.mode_bits_count();
51 let length_bits_count = self.mode.length_bits_count(version);
52 let data_bits_count = self.mode.data_bits_count(chars_count);
53
54 mode_bits_count + length_bits_count + data_bits_count
55 }
56}
57
58struct EcsIter<I> {
73 base: I,
74 index: usize,
75 ended: bool,
76}
77
78impl<'a, I: Iterator<Item = &'a u8>> Iterator for EcsIter<I> {
79 type Item = (usize, ExclCharSet);
80
81 fn next(&mut self) -> Option<(usize, ExclCharSet)> {
82 if self.ended {
83 return None;
84 }
85
86 match self.base.next() {
87 None => {
88 self.ended = true;
89 Some((self.index, ExclCharSet::End))
90 }
91 Some(c) => {
92 let old_index = self.index;
93 self.index += 1;
94 Some((old_index, ExclCharSet::from_u8(*c)))
95 }
96 }
97 }
98}
99
100pub struct Parser<'a> {
102 ecs_iter: EcsIter<Iter<'a, u8>>,
103 state: State,
104 begin: usize,
105 pending_single_byte: bool,
106}
107
108impl<'a> Parser<'a> {
109 pub fn new(data: &[u8]) -> Parser<'_> {
121 Parser {
122 ecs_iter: EcsIter { base: data.iter(), index: 0, ended: false },
123 state: State::Init,
124 begin: 0,
125 pending_single_byte: false,
126 }
127 }
128}
129
130impl<'a> Iterator for Parser<'a> {
131 type Item = Segment;
132
133 fn next(&mut self) -> Option<Segment> {
134 if self.pending_single_byte {
135 self.pending_single_byte = false;
136 self.begin += 1;
137 return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin });
138 }
139
140 loop {
141 let (i, ecs) = self.ecs_iter.next()?;
142 let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
143 self.state = next_state;
144
145 let old_begin = self.begin;
146 let push_mode = match action {
147 Action::Idle => continue,
148 Action::Numeric => Mode::Numeric,
149 Action::Alpha => Mode::Alphanumeric,
150 Action::Byte => Mode::Byte,
151 Action::Kanji => Mode::Kanji,
152 Action::KanjiAndSingleByte => {
153 let next_begin = i - 1;
154 if self.begin == next_begin {
155 Mode::Byte
156 } else {
157 self.pending_single_byte = true;
158 self.begin = next_begin;
159 return Some(Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin });
160 }
161 }
162 };
163
164 self.begin = i;
165 return Some(Segment { mode: push_mode, begin: old_begin, end: i });
166 }
167 }
168}
169
170#[cfg(test)]
171mod parse_tests {
172 use crate::optimize::{Parser, Segment};
173 use crate::types::Mode;
174
175 fn parse(data: &[u8]) -> Vec<Segment> {
176 Parser::new(data).collect()
177 }
178
179 #[test]
180 fn test_parse_1() {
181 let segs = parse(b"01049123451234591597033130128%10ABC123");
182 assert_eq!(
183 segs,
184 vec![
185 Segment { mode: Mode::Numeric, begin: 0, end: 29 },
186 Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
187 Segment { mode: Mode::Numeric, begin: 30, end: 32 },
188 Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
189 Segment { mode: Mode::Numeric, begin: 35, end: 38 },
190 ]
191 );
192 }
193
194 #[test]
195 fn test_parse_shift_jis_example_1() {
196 let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); assert_eq!(
198 segs,
199 vec![
200 Segment { mode: Mode::Kanji, begin: 0, end: 4 },
201 Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
202 Segment { mode: Mode::Byte, begin: 5, end: 6 },
203 Segment { mode: Mode::Kanji, begin: 6, end: 8 },
204 ]
205 );
206 }
207
208 #[test]
209 fn test_parse_utf_8() {
210 let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
212 assert_eq!(
213 segs,
214 vec![
215 Segment { mode: Mode::Kanji, begin: 0, end: 4 },
216 Segment { mode: Mode::Byte, begin: 4, end: 5 },
217 Segment { mode: Mode::Kanji, begin: 5, end: 7 },
218 Segment { mode: Mode::Byte, begin: 7, end: 10 },
219 Segment { mode: Mode::Kanji, begin: 10, end: 12 },
220 Segment { mode: Mode::Byte, begin: 12, end: 13 },
221 ]
222 );
223 }
224
225 #[test]
226 fn test_not_kanji_1() {
227 let segs = parse(b"\x81\x30");
228 assert_eq!(
229 segs,
230 vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Numeric, begin: 1, end: 2 }]
231 );
232 }
233
234 #[test]
235 fn test_not_kanji_2() {
236 let segs = parse(b"\xeb\xc0");
239 assert_eq!(
240 segs,
241 vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
242 );
243 }
244
245 #[test]
246 fn test_not_kanji_3() {
247 let segs = parse(b"\x81\x7f");
248 assert_eq!(
249 segs,
250 vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
251 );
252 }
253
254 #[test]
255 fn test_not_kanji_4() {
256 let segs = parse(b"\x81\x40\x81");
257 assert_eq!(
258 segs,
259 vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 }, Segment { mode: Mode::Byte, begin: 2, end: 3 }]
260 );
261 }
262}
263
264pub struct Optimizer<I> {
271 optimized: Vec<Segment>,
272 index: usize,
273 _source: PhantomData<I>,
274}
275
276impl<I: Iterator<Item = Segment>> Optimizer<I> {
277 pub fn new(segments: I, version: Version) -> Self {
284 let segments = segments.collect::<Vec<_>>();
285 Self { optimized: optimize_segments(&segments, version), index: 0, _source: PhantomData }
286 }
287}
288
289impl<'a> Parser<'a> {
290 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 let segment = self.optimized.get(self.index).copied();
302 if segment.is_some() {
303 self.index += 1;
304 }
305 segment
306 }
307
308 fn size_hint(&self) -> (usize, Option<usize>) {
309 let remaining = self.optimized.len().saturating_sub(self.index);
310 (remaining, Some(remaining))
311 }
312}
313
314impl<I: Iterator<Item = Segment>> ExactSizeIterator for Optimizer<I> {}
315impl<I: Iterator<Item = Segment>> core::iter::FusedIterator for Optimizer<I> {}
316
317pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
319 segments.iter().map(|seg| seg.encoded_len(version)).sum()
320}
321
322#[must_use]
331pub fn optimize_segments(segments: &[Segment], version: Version) -> Vec<Segment> {
332 if segments.len() <= 32 || !supports_linear_costs(segments, version) {
333 optimize_segments_quadratic(segments, version)
334 } else {
335 optimize_segments_linear(segments, version)
336 }
337}
338
339#[derive(Clone, Copy)]
340struct ModeCost {
341 mode: Mode,
342 period: usize,
343 bits_per_period: i128,
344 bucket_offset: usize,
345}
346
347const MODE_COSTS: [ModeCost; 4] = [
348 ModeCost { mode: Mode::Numeric, period: 3, bits_per_period: 10, bucket_offset: 0 },
349 ModeCost { mode: Mode::Alphanumeric, period: 2, bits_per_period: 11, bucket_offset: 3 },
350 ModeCost { mode: Mode::Byte, period: 1, bits_per_period: 8, bucket_offset: 5 },
351 ModeCost { mode: Mode::Kanji, period: 2, bits_per_period: 13, bucket_offset: 6 },
352];
353
354fn mode_index(mode: Mode) -> usize {
355 match mode {
356 Mode::Numeric => 0,
357 Mode::Alphanumeric => 1,
358 Mode::Byte => 2,
359 Mode::Kanji => 3,
360 }
361}
362
363fn supports_linear_costs(segments: &[Segment], version: Version) -> bool {
364 let mut min_begin = usize::MAX;
367 let mut max_begin = 0;
368 let mut max_end = 0;
369 for segment in segments {
370 min_begin = min_begin.min(segment.begin);
371 max_begin = max_begin.max(segment.begin);
372 max_end = max_end.max(segment.end);
373 if segment.end < max_begin {
374 return false;
375 }
376 }
377 let max_header = MODE_COSTS
378 .iter()
379 .map(|cost| version.mode_bits_count() + cost.mode.length_bits_count(version))
380 .max()
381 .unwrap_or(0);
382 (max_end - min_begin).checked_mul(13).and_then(|bits| bits.checked_add(max_header)).is_some()
383}
384
385fn prefer_start(
386 candidate: usize,
387 current: usize,
388 cost: ModeCost,
389 segments: &[Segment],
390 best_bits: &[usize],
391 best_count: &[usize],
392) -> bool {
393 if current == usize::MAX {
394 return true;
395 }
396 let key = |start: usize| {
399 (
400 best_bits[start] as i128 - (segments[start].begin / cost.period) as i128 * cost.bits_per_period,
401 best_count[start],
402 Reverse(start),
403 )
404 };
405 key(candidate) < key(current)
406}
407
408fn optimize_segments_linear(segments: &[Segment], version: Version) -> Vec<Segment> {
409 let len = segments.len();
410 if len <= 1 {
411 return segments.to_vec();
412 }
413 let mut best_bits = vec![usize::MAX; len + 1];
414 let mut best_count = vec![usize::MAX; len + 1];
415 let mut previous = vec![0_usize; len + 1];
416 let mut previous_mode = vec![Mode::Byte; len + 1];
417 best_bits[0] = 0;
418 best_count[0] = 0;
419
420 let mut groups = [[usize::MAX; 8]; 4];
423 for end in 1..=len {
424 let incoming = segments[end - 1].mode;
425 for (source_index, source_cost) in MODE_COSTS.iter().enumerate() {
428 let destination = source_cost.mode.max(incoming);
429 if destination == source_cost.mode {
430 continue;
431 }
432 let migrating = core::mem::replace(&mut groups[source_index], [usize::MAX; 8]);
433 let destination_group = &mut groups[mode_index(destination)];
434 for cost in MODE_COSTS {
435 for bucket in cost.bucket_offset..cost.bucket_offset + cost.period {
436 let start = migrating[bucket];
437 if start != usize::MAX
438 && prefer_start(start, destination_group[bucket], cost, segments, &best_bits, &best_count)
439 {
440 destination_group[bucket] = start;
441 }
442 }
443 }
444 }
445
446 let start = end - 1;
447 if best_bits[start] != usize::MAX {
448 for cost in MODE_COSTS {
449 let bucket = cost.bucket_offset + segments[start].begin % cost.period;
450 let group = &mut groups[mode_index(incoming)];
451 if prefer_start(start, group[bucket], cost, segments, &best_bits, &best_count) {
452 group[bucket] = start;
453 }
454 }
455 }
456
457 for (group_index, cost) in MODE_COSTS.iter().enumerate() {
458 for &start in &groups[group_index][cost.bucket_offset..cost.bucket_offset + cost.period] {
459 if start == usize::MAX {
460 continue;
461 }
462 let merged = Segment { mode: cost.mode, begin: segments[start].begin, end: segments[end - 1].end };
463 let Some(candidate_bits) = best_bits[start].checked_add(merged.encoded_len(version)) else {
464 continue;
465 };
466 let candidate_count = best_count[start] + 1;
467 if (candidate_bits, candidate_count, Reverse(start))
470 < (best_bits[end], best_count[end], Reverse(previous[end]))
471 {
472 best_bits[end] = candidate_bits;
473 best_count[end] = candidate_count;
474 previous[end] = start;
475 previous_mode[end] = cost.mode;
476 }
477 }
478 }
479 }
480
481 let mut cursor = len;
482 let mut optimized = Vec::with_capacity(best_count[len]);
483 while cursor > 0 {
484 let start = previous[cursor];
485 optimized.push(Segment {
486 mode: previous_mode[cursor],
487 begin: segments[start].begin,
488 end: segments[cursor - 1].end,
489 });
490 cursor = start;
491 }
492 optimized.reverse();
493 optimized
494}
495
496fn optimize_segments_quadratic(segments: &[Segment], version: Version) -> Vec<Segment> {
497 let len = segments.len();
498 if len <= 1 {
499 return segments.to_vec();
500 }
501
502 let mut best_bits = vec![usize::MAX; len + 1];
503 let mut best_count = vec![usize::MAX; len + 1];
504 let mut previous = vec![0_usize; len + 1];
505 let mut previous_mode = vec![Mode::Byte; len + 1];
506 best_bits[0] = 0;
507 best_count[0] = 0;
508
509 for end in 1..=len {
510 let mut mode = segments[end - 1].mode;
511 for start in (0..end).rev() {
512 if start + 1 < end {
513 mode = segments[start].mode.max(mode);
514 }
515 let merged = Segment { mode, begin: segments[start].begin, end: segments[end - 1].end };
516 let Some(candidate_bits) = best_bits[start].checked_add(merged.encoded_len(version)) else {
517 continue;
518 };
519 let candidate_count = best_count[start] + 1;
520 if candidate_bits < best_bits[end] || candidate_bits == best_bits[end] && candidate_count < best_count[end]
521 {
522 best_bits[end] = candidate_bits;
523 best_count[end] = candidate_count;
524 previous[end] = start;
525 previous_mode[end] = mode;
526 }
527 }
528 }
529
530 let mut cursor = len;
531 let mut optimized = Vec::with_capacity(best_count[len]);
532 while cursor > 0 {
533 let start = previous[cursor];
534 optimized.push(Segment {
535 mode: previous_mode[cursor],
536 begin: segments[start].begin,
537 end: segments[cursor - 1].end,
538 });
539 cursor = start;
540 }
541 optimized.reverse();
542 optimized
543}
544
545#[cfg(test)]
546mod optimize_tests {
547 use crate::optimize::{
548 Optimizer, Parser, Segment, optimize_segments, optimize_segments_linear, optimize_segments_quadratic,
549 supports_linear_costs, total_encoded_len,
550 };
551 use crate::types::{Mode, Version};
552
553 fn test_optimization_result(given: &[Segment], expected: &[Segment], version: Version) {
554 let prev_len = total_encoded_len(given, version);
555 let opt_segs = Optimizer::new(given.iter().copied(), version).collect::<Vec<_>>();
556 let new_len = total_encoded_len(&opt_segs, version);
557 if given != opt_segs {
558 assert!(prev_len > new_len, "{prev_len} > {new_len}");
559 }
560 assert_eq!(
561 opt_segs,
562 expected,
563 "Optimization gave something better: {} < {} ({:?})",
564 new_len,
565 total_encoded_len(expected, version),
566 opt_segs
567 );
568 }
569
570 #[test]
571 fn zero_or_one_segment_preserves_the_input_for_every_version_group() {
572 let versions =
573 [Version::Normal(1), Version::Normal(10), Version::Normal(27), Version::Micro(1), Version::Micro(4)];
574 for version in versions {
575 assert!(optimize_segments(&[], version).is_empty());
576 for mode in [Mode::Numeric, Mode::Alphanumeric, Mode::Byte, Mode::Kanji] {
577 let segment = Segment { mode, begin: 12, end: 30 };
578 assert_eq!(optimize_segments(&[segment], version), vec![segment]);
579 }
580 }
581 }
582
583 #[test]
584 fn test_example_1() {
585 test_optimization_result(
586 &[
587 Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
588 Segment { mode: Mode::Numeric, begin: 3, end: 6 },
589 Segment { mode: Mode::Byte, begin: 6, end: 10 },
590 ],
591 &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 }, Segment { mode: Mode::Byte, begin: 6, end: 10 }],
592 Version::Normal(1),
593 );
594 }
595
596 #[test]
597 fn test_example_2() {
598 test_optimization_result(
599 &[
600 Segment { mode: Mode::Numeric, begin: 0, end: 29 },
601 Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
602 Segment { mode: Mode::Numeric, begin: 30, end: 32 },
603 Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
604 Segment { mode: Mode::Numeric, begin: 35, end: 38 },
605 ],
606 &[
607 Segment { mode: Mode::Numeric, begin: 0, end: 29 },
608 Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 },
609 ],
610 Version::Normal(9),
611 );
612 }
613
614 #[test]
615 fn test_example_3() {
616 test_optimization_result(
617 &[
618 Segment { mode: Mode::Kanji, begin: 0, end: 4 },
619 Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
620 Segment { mode: Mode::Byte, begin: 5, end: 6 },
621 Segment { mode: Mode::Kanji, begin: 6, end: 8 },
622 ],
623 &[Segment { mode: Mode::Byte, begin: 0, end: 8 }],
624 Version::Normal(1),
625 );
626 }
627
628 #[test]
629 fn test_example_4() {
630 test_optimization_result(
631 &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
632 &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
633 Version::Normal(1),
634 );
635 }
636
637 #[test]
638 fn test_annex_j_guideline_1a() {
639 test_optimization_result(
640 &[
641 Segment { mode: Mode::Numeric, begin: 0, end: 3 },
642 Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
643 ],
644 &[
645 Segment { mode: Mode::Numeric, begin: 0, end: 3 },
646 Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
647 ],
648 Version::Micro(2),
649 );
650 }
651
652 #[test]
653 fn test_annex_j_guideline_1b() {
654 test_optimization_result(
655 &[
656 Segment { mode: Mode::Numeric, begin: 0, end: 2 },
657 Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 },
658 ],
659 &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
660 Version::Micro(2),
661 );
662 }
663
664 #[test]
665 fn test_annex_j_guideline_1c() {
666 test_optimization_result(
667 &[
668 Segment { mode: Mode::Numeric, begin: 0, end: 3 },
669 Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
670 ],
671 &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
672 Version::Micro(3),
673 );
674 }
675
676 #[test]
677 fn dynamic_programming_can_skip_a_local_merge_for_a_better_total() {
678 let given = [
679 Segment { mode: Mode::Numeric, begin: 0, end: 7 },
680 Segment { mode: Mode::Alphanumeric, begin: 7, end: 8 },
681 Segment { mode: Mode::Numeric, begin: 8, end: 9 },
682 ];
683
684 let optimized = optimize_segments(&given, Version::Normal(1));
685
686 assert_eq!(
687 optimized,
688 vec![
689 Segment { mode: Mode::Numeric, begin: 0, end: 7 },
690 Segment { mode: Mode::Alphanumeric, begin: 7, end: 9 },
691 ]
692 );
693 assert!(
694 total_encoded_len(&optimized, Version::Normal(1))
695 < total_encoded_len(&[Segment { mode: Mode::Alphanumeric, begin: 0, end: 9 }], Version::Normal(1))
696 );
697 }
698
699 const COST_VERSIONS: [Version; 7] = [
700 Version::Normal(1),
701 Version::Normal(10),
702 Version::Normal(27),
703 Version::Micro(1),
704 Version::Micro(2),
705 Version::Micro(3),
706 Version::Micro(4),
707 ];
708 const MODES: [Mode; 4] = [Mode::Numeric, Mode::Alphanumeric, Mode::Byte, Mode::Kanji];
709
710 fn assert_matches_quadratic(segments: &[Segment], version: Version) {
711 let expected = optimize_segments_quadratic(segments, version);
712 assert_eq!(
713 optimize_segments_linear(segments, version),
714 expected,
715 "linear plan: version {version:?}, segments {segments:?}"
716 );
717 assert_eq!(
718 optimize_segments(segments, version),
719 expected,
720 "hybrid plan: version {version:?}, segments {segments:?}"
721 );
722 }
723
724 #[test]
725 fn linear_plan_matches_quadratic_for_exhaustive_modes_and_length_residues() {
726 for len in 0..=6 {
727 for mut encoded_modes in 0..4_usize.pow(len) {
728 let modes = (0..len)
729 .map(|_| {
730 let mode = MODES[encoded_modes % 4];
731 encoded_modes /= 4;
732 mode
733 })
734 .collect::<Vec<_>>();
735 for length_pattern in 0..6 {
736 let mut begin = 17;
737 let segments = modes
738 .iter()
739 .enumerate()
740 .map(|(index, &mode)| {
741 let length = match length_pattern {
742 0..=3 => length_pattern,
743 4 => 7,
744 _ => index % 6 + 1,
745 };
746 let segment = Segment { mode, begin, end: begin + length };
747 begin = segment.end;
748 segment
749 })
750 .collect::<Vec<_>>();
751 for version in COST_VERSIONS {
752 assert_matches_quadratic(&segments, version);
753 }
754 }
755 }
756 }
757 }
758
759 fn next_random(seed: &mut u64) -> u64 {
760 *seed ^= *seed << 13;
761 *seed ^= *seed >> 7;
762 *seed ^= *seed << 17;
763 *seed
764 }
765
766 #[test]
767 fn linear_plan_matches_quadratic_for_long_random_sequences_and_shifted_offsets() {
768 let versions = [
769 Version::Normal(1),
770 Version::Normal(9),
771 Version::Normal(10),
772 Version::Normal(26),
773 Version::Normal(27),
774 Version::Normal(40),
775 Version::Micro(1),
776 Version::Micro(2),
777 Version::Micro(3),
778 Version::Micro(4),
779 ];
780 let mut seed = 2_712_u64;
781 for case in 0..512 {
782 let len = next_random(&mut seed) as usize % 224 + 33;
783 let mut begin = if case % 8 == 0 { usize::MAX - 65_536 } else { next_random(&mut seed) as usize % 1024 };
784 let segments = (0..len)
785 .map(|_| {
786 let mode = MODES[next_random(&mut seed) as usize % 4];
787 let length = next_random(&mut seed) as usize % 71;
788 let gap = next_random(&mut seed) as usize % 5;
789 let segment = Segment { mode, begin, end: begin + length };
790 begin = segment.end + gap;
791 segment
792 })
793 .collect::<Vec<_>>();
794 assert!(supports_linear_costs(&segments, versions[case % versions.len()]));
795 assert_matches_quadratic(&segments, versions[case % versions.len()]);
796 }
797 }
798
799 #[test]
800 fn linear_plan_matches_quadratic_at_the_hybrid_boundary() {
801 for len in [31, 32, 33, 64, 256, 1024] {
802 let data = (0..len).map(|index| if index % 2 == 0 { b'A' } else { b'1' }).collect::<Vec<_>>();
803 let segments = Parser::new(&data).collect::<Vec<_>>();
804 for version in COST_VERSIONS {
805 assert_matches_quadratic(&segments, version);
806 }
807 }
808 }
809
810 #[test]
811 fn linear_plan_keeps_the_rightmost_start_when_bits_and_segment_count_tie() {
812 let given = [
813 Segment { mode: Mode::Numeric, begin: 0, end: 7 },
814 Segment { mode: Mode::Alphanumeric, begin: 7, end: 8 },
815 Segment { mode: Mode::Numeric, begin: 8, end: 15 },
816 ];
817 let expected = vec![
818 Segment { mode: Mode::Alphanumeric, begin: 0, end: 8 },
819 Segment { mode: Mode::Numeric, begin: 8, end: 15 },
820 ];
821 let optimized = optimize_segments_linear(&given, Version::Normal(1));
822 assert_eq!(total_encoded_len(&optimized, Version::Normal(1)), 95);
823 assert_eq!(optimized, expected);
824 assert_matches_quadratic(&given, Version::Normal(1));
825 }
826
827 #[test]
828 fn exceptional_coordinates_preserve_the_quadratic_fallback() {
829 for segments in [
830 vec![Segment { mode: Mode::Byte, begin: 2, end: 1 }; 33],
831 (0..33)
832 .map(|index| {
833 let begin = if index == 0 { 0 } else { usize::MAX / 4 };
834 Segment { mode: Mode::Byte, begin, end: begin }
835 })
836 .collect::<Vec<_>>(),
837 ] {
838 assert!(!supports_linear_costs(&segments, Version::Normal(1)));
839 let actual = std::panic::catch_unwind(|| optimize_segments(&segments, Version::Normal(1)));
840 let expected = std::panic::catch_unwind(|| optimize_segments_quadratic(&segments, Version::Normal(1)));
841 match (actual, expected) {
842 (Ok(actual), Ok(expected)) => assert_eq!(actual, expected),
843 (Err(_), Err(_)) => {}
844 _ => panic!("the hybrid path changed the exceptional-coordinate behavior"),
845 }
846 }
847 }
848}
849
850#[derive(Copy, Clone)]
858enum ExclCharSet {
859 End = 0,
861
862 Symbol = 1,
865
866 Numeric = 2,
868
869 Alpha = 3,
872
873 KanjiHi1 = 4,
875
876 KanjiHi2 = 5,
878
879 KanjiHi3 = 6,
883
884 KanjiLo1 = 7,
888
889 KanjiLo2 = 8,
894
895 Byte = 9,
897}
898
899impl ExclCharSet {
900 fn from_u8(c: u8) -> Self {
902 match c {
903 0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d..=0x2f | 0x3a => ExclCharSet::Symbol,
904 0x30..=0x39 => ExclCharSet::Numeric,
905 0x41..=0x5a => ExclCharSet::Alpha,
906 0x81..=0x9f => ExclCharSet::KanjiHi1,
907 0xe0..=0xea => ExclCharSet::KanjiHi2,
908 0xeb => ExclCharSet::KanjiHi3,
909 0x40 | 0x5b..=0x7e | 0x80 | 0xa0..=0xbf => ExclCharSet::KanjiLo1,
910 0xc0..=0xdf | 0xec..=0xfc => ExclCharSet::KanjiLo2,
911 _ => ExclCharSet::Byte,
912 }
913 }
914}
915
916#[derive(Copy, Clone)]
918enum State {
919 Init = 0,
921
922 Numeric = 10,
924
925 Alpha = 20,
927
928 Byte = 30,
930
931 KanjiHi12 = 40,
934
935 KanjiHi3 = 50,
938
939 Kanji = 60,
941}
942
943#[derive(Copy, Clone)]
945enum Action {
946 Idle,
948
949 Numeric,
951
952 Alpha,
954
955 Byte,
957
958 Kanji,
960
961 KanjiAndSingleByte,
964}
965
966static STATE_TRANSITION: [(State, Action); 70] = [
967 (State::Init, Action::Idle), (State::Alpha, Action::Idle), (State::Numeric, Action::Idle), (State::Alpha, Action::Idle), (State::KanjiHi12, Action::Idle), (State::KanjiHi12, Action::Idle), (State::KanjiHi3, Action::Idle), (State::Byte, Action::Idle), (State::Byte, Action::Idle), (State::Byte, Action::Idle), (State::Init, Action::Numeric), (State::Alpha, Action::Numeric), (State::Numeric, Action::Idle), (State::Alpha, Action::Numeric), (State::KanjiHi12, Action::Numeric), (State::KanjiHi12, Action::Numeric), (State::KanjiHi3, Action::Numeric), (State::Byte, Action::Numeric), (State::Byte, Action::Numeric), (State::Byte, Action::Numeric), (State::Init, Action::Alpha), (State::Alpha, Action::Idle), (State::Numeric, Action::Alpha), (State::Alpha, Action::Idle), (State::KanjiHi12, Action::Alpha), (State::KanjiHi12, Action::Alpha), (State::KanjiHi3, Action::Alpha), (State::Byte, Action::Alpha), (State::Byte, Action::Alpha), (State::Byte, Action::Alpha), (State::Init, Action::Byte), (State::Alpha, Action::Byte), (State::Numeric, Action::Byte), (State::Alpha, Action::Byte), (State::KanjiHi12, Action::Byte), (State::KanjiHi12, Action::Byte), (State::KanjiHi3, Action::Byte), (State::Byte, Action::Idle), (State::Byte, Action::Idle), (State::Byte, Action::Idle), (State::Init, Action::KanjiAndSingleByte), (State::Alpha, Action::KanjiAndSingleByte), (State::Numeric, Action::KanjiAndSingleByte), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::Byte, Action::KanjiAndSingleByte), (State::Init, Action::KanjiAndSingleByte), (State::Alpha, Action::KanjiAndSingleByte), (State::Numeric, Action::KanjiAndSingleByte), (State::Kanji, Action::Idle), (State::Kanji, Action::Idle), (State::KanjiHi12, Action::KanjiAndSingleByte), (State::KanjiHi3, Action::KanjiAndSingleByte), (State::Kanji, Action::Idle), (State::Byte, Action::KanjiAndSingleByte), (State::Byte, Action::KanjiAndSingleByte), (State::Init, Action::Kanji), (State::Alpha, Action::Kanji), (State::Numeric, Action::Kanji), (State::Alpha, Action::Kanji), (State::KanjiHi12, Action::Idle), (State::KanjiHi12, Action::Idle), (State::KanjiHi3, Action::Idle), (State::Byte, Action::Kanji), (State::Byte, Action::Kanji), (State::Byte, Action::Kanji), ];
1047
1048