1use base64::Engine;
21use byteorder::{BigEndian, WriteBytesExt};
22use hex;
23use quick_xml::Reader;
24use quick_xml::events::Event;
25use std::collections::HashMap;
26use std::io::{self, BufRead, Write};
27use thiserror::Error;
28
29#[derive(Error, Debug)]
30pub enum ConversionError {
31 #[error("XML parsing failed: {0}")]
32 XmlParsing(#[from] quick_xml::Error),
33 #[error("IO error: {0}")]
34 Io(#[from] io::Error),
35 #[error("String too long: {0} bytes (max: {1})")]
36 StringTooLong(usize, usize),
37 #[error("Binary data too long: {0} bytes (max: {1})")]
38 BinaryDataTooLong(usize, usize),
39 #[error("Invalid hex string")]
40 InvalidHex,
41 #[error("Invalid base64 string")]
42 InvalidBase64,
43 #[error("UTF-8 conversion error: {0}")]
44 Utf8Error(#[from] std::str::Utf8Error),
45 #[error("Attribute error: {0}")]
46 AttrError(#[from] quick_xml::events::attributes::AttrError),
47}
48
49pub fn show_warning(feature: &str, details: Option<&str>) {
51 eprintln!("WARNING: {} is not supported and might be lost.", feature);
52 if let Some(details) = details {
53 eprintln!(" {}", details);
54 }
55}
56
57pub struct FastDataOutput<W: Write> {
58 writer: W,
59 string_pool: HashMap<String, u16>,
60 interned_strings: Vec<String>,
61}
62
63impl<W: Write> FastDataOutput<W> {
64 pub const MAX_UNSIGNED_SHORT: u16 = 65535;
65
66 pub fn new(writer: W) -> Self {
67 Self {
68 writer,
69 string_pool: HashMap::new(),
70 interned_strings: Vec::new(),
71 }
72 }
73
74 pub fn write_byte(&mut self, value: u8) -> Result<(), ConversionError> {
75 self.writer.write_u8(value)?;
76 Ok(())
77 }
78
79 pub fn write_short(&mut self, value: u16) -> Result<(), ConversionError> {
80 self.writer.write_u16::<BigEndian>(value)?;
81 Ok(())
82 }
83
84 pub fn write_int(&mut self, value: i32) -> Result<(), ConversionError> {
85 self.writer.write_i32::<BigEndian>(value)?;
86 Ok(())
87 }
88
89 pub fn write_long(&mut self, value: i64) -> Result<(), ConversionError> {
90 self.writer.write_i64::<BigEndian>(value)?;
91 Ok(())
92 }
93
94 pub fn write_float(&mut self, value: f32) -> Result<(), ConversionError> {
95 self.writer.write_f32::<BigEndian>(value)?;
96 Ok(())
97 }
98
99 pub fn write_double(&mut self, value: f64) -> Result<(), ConversionError> {
100 self.writer.write_f64::<BigEndian>(value)?;
101 Ok(())
102 }
103
104 pub fn write_utf(&mut self, s: &str) -> Result<(), ConversionError> {
105 let bytes = s.as_bytes();
106 if bytes.len() > Self::MAX_UNSIGNED_SHORT as usize {
107 return Err(ConversionError::StringTooLong(
108 bytes.len(),
109 Self::MAX_UNSIGNED_SHORT as usize,
110 ));
111 }
112 self.write_short(bytes.len() as u16)?;
113 self.writer.write_all(bytes)?;
114 Ok(())
115 }
116
117 pub fn write_interned_utf(&mut self, s: &str) -> Result<(), ConversionError> {
118 if let Some(&index) = self.string_pool.get(s) {
119 self.write_short(index)?;
120 } else {
121 self.write_short(0xFFFF)?;
122 self.write_utf(s)?;
123 let index = self.interned_strings.len() as u16;
124 self.string_pool.insert(s.to_string(), index);
125 self.interned_strings.push(s.to_string());
126 }
127 Ok(())
128 }
129
130 pub fn write_bytes(&mut self, data: &[u8]) -> Result<(), ConversionError> {
131 self.writer.write_all(data)?;
132 Ok(())
133 }
134
135 pub fn flush(&mut self) -> Result<(), ConversionError> {
136 self.writer.flush()?;
137 Ok(())
138 }
139}
140
141pub struct BinaryXmlSerializer<W: Write> {
142 output: FastDataOutput<W>,
143 tag_count: usize,
144 tag_names: Vec<String>,
145 preserve_whitespace: bool,
146}
147
148impl<W: Write> BinaryXmlSerializer<W> {
150 pub const PROTOCOL_MAGIC_VERSION_0: [u8; 4] = [0x41, 0x42, 0x58, 0x00];
151 pub const START_DOCUMENT: u8 = 0;
152 pub const END_DOCUMENT: u8 = 1;
153 pub const START_TAG: u8 = 2;
154 pub const END_TAG: u8 = 3;
155 pub const TEXT: u8 = 4;
156 pub const CDSECT: u8 = 5;
157 pub const ENTITY_REF: u8 = 6;
158 pub const IGNORABLE_WHITESPACE: u8 = 7;
159 pub const PROCESSING_INSTRUCTION: u8 = 8;
160 pub const COMMENT: u8 = 9;
161 pub const DOCDECL: u8 = 10;
162 pub const ATTRIBUTE: u8 = 15;
163
164 pub const TYPE_NULL: u8 = 1 << 4;
165 pub const TYPE_STRING: u8 = 2 << 4;
166 pub const TYPE_STRING_INTERNED: u8 = 3 << 4;
167 pub const TYPE_BYTES_HEX: u8 = 4 << 4;
168 pub const TYPE_BYTES_BASE64: u8 = 5 << 4;
169 pub const TYPE_INT: u8 = 6 << 4;
170 pub const TYPE_INT_HEX: u8 = 7 << 4;
171 pub const TYPE_LONG: u8 = 8 << 4;
172 pub const TYPE_LONG_HEX: u8 = 9 << 4;
173 pub const TYPE_FLOAT: u8 = 10 << 4;
174 pub const TYPE_DOUBLE: u8 = 11 << 4;
175 pub const TYPE_BOOLEAN_TRUE: u8 = 12 << 4;
176 pub const TYPE_BOOLEAN_FALSE: u8 = 13 << 4;
177
178 pub fn new(writer: W) -> Result<Self, ConversionError> {
179 Self::with_options(writer, true)
180 }
181
182 pub fn with_options(writer: W, preserve_whitespace: bool) -> Result<Self, ConversionError> {
183 let mut output = FastDataOutput::new(writer);
184 output.write_bytes(&Self::PROTOCOL_MAGIC_VERSION_0)?;
185
186 Ok(Self {
187 output,
188 tag_count: 0,
189 tag_names: Vec::with_capacity(8),
190 preserve_whitespace,
191 })
192 }
193
194 fn write_token(&mut self, token: u8, text: Option<&str>) -> Result<(), ConversionError> {
195 if let Some(text) = text {
196 self.output.write_byte(token | Self::TYPE_STRING)?;
197 self.output.write_utf(text)?;
198 } else {
199 self.output.write_byte(token | Self::TYPE_NULL)?;
200 }
201 Ok(())
202 }
203
204 pub fn start_document(&mut self) -> Result<(), ConversionError> {
205 self.output
206 .write_byte(Self::START_DOCUMENT | Self::TYPE_NULL)
207 }
208
209 pub fn end_document(&mut self) -> Result<(), ConversionError> {
210 self.output
211 .write_byte(Self::END_DOCUMENT | Self::TYPE_NULL)?;
212 self.output.flush()
213 }
214
215 pub fn start_tag(&mut self, name: &str) -> Result<(), ConversionError> {
216 if self.tag_count == self.tag_names.len() {
217 let new_size = self.tag_count + std::cmp::max(1, self.tag_count / 2);
218 self.tag_names.resize(new_size, String::new());
219 }
220 self.tag_names[self.tag_count] = name.to_string();
221 self.tag_count += 1;
222
223 self.output
224 .write_byte(Self::START_TAG | Self::TYPE_STRING_INTERNED)?;
225 self.output.write_interned_utf(name)
226 }
227
228 pub fn end_tag(&mut self, name: &str) -> Result<(), ConversionError> {
229 self.tag_count -= 1;
230 self.output
231 .write_byte(Self::END_TAG | Self::TYPE_STRING_INTERNED)?;
232 self.output.write_interned_utf(name)
233 }
234
235 pub fn attribute(&mut self, name: &str, value: &str) -> Result<(), ConversionError> {
236 self.output
237 .write_byte(Self::ATTRIBUTE | Self::TYPE_STRING)?;
238 self.output.write_interned_utf(name)?;
239 self.output.write_utf(value)
240 }
241
242 pub fn attribute_interned(&mut self, name: &str, value: &str) -> Result<(), ConversionError> {
243 self.output
244 .write_byte(Self::ATTRIBUTE | Self::TYPE_STRING_INTERNED)?;
245 self.output.write_interned_utf(name)?;
246 self.output.write_interned_utf(value)
247 }
248
249 pub fn attribute_bytes_hex(&mut self, name: &str, value: &[u8]) -> Result<(), ConversionError> {
250 if value.len() > FastDataOutput::<W>::MAX_UNSIGNED_SHORT as usize {
251 return Err(ConversionError::BinaryDataTooLong(
252 value.len(),
253 FastDataOutput::<W>::MAX_UNSIGNED_SHORT as usize,
254 ));
255 }
256 self.output
257 .write_byte(Self::ATTRIBUTE | Self::TYPE_BYTES_HEX)?;
258 self.output.write_interned_utf(name)?;
259 self.output.write_short(value.len() as u16)?;
260 self.output.write_bytes(value)
261 }
262
263 pub fn attribute_bytes_base64(
264 &mut self,
265 name: &str,
266 value: &[u8],
267 ) -> Result<(), ConversionError> {
268 if value.len() > FastDataOutput::<W>::MAX_UNSIGNED_SHORT as usize {
269 return Err(ConversionError::BinaryDataTooLong(
270 value.len(),
271 FastDataOutput::<W>::MAX_UNSIGNED_SHORT as usize,
272 ));
273 }
274 self.output
275 .write_byte(Self::ATTRIBUTE | Self::TYPE_BYTES_BASE64)?;
276 self.output.write_interned_utf(name)?;
277 self.output.write_short(value.len() as u16)?;
278 self.output.write_bytes(value)
279 }
280
281 pub fn attribute_int(&mut self, name: &str, value: i32) -> Result<(), ConversionError> {
282 self.output.write_byte(Self::ATTRIBUTE | Self::TYPE_INT)?;
283 self.output.write_interned_utf(name)?;
284 self.output.write_int(value)
285 }
286
287 pub fn attribute_int_hex(&mut self, name: &str, value: i32) -> Result<(), ConversionError> {
288 self.output
289 .write_byte(Self::ATTRIBUTE | Self::TYPE_INT_HEX)?;
290 self.output.write_interned_utf(name)?;
291 self.output.write_int(value)
292 }
293
294 pub fn attribute_long(&mut self, name: &str, value: i64) -> Result<(), ConversionError> {
295 self.output.write_byte(Self::ATTRIBUTE | Self::TYPE_LONG)?;
296 self.output.write_interned_utf(name)?;
297 self.output.write_long(value)
298 }
299
300 pub fn attribute_long_hex(&mut self, name: &str, value: i64) -> Result<(), ConversionError> {
301 self.output
302 .write_byte(Self::ATTRIBUTE | Self::TYPE_LONG_HEX)?;
303 self.output.write_interned_utf(name)?;
304 self.output.write_long(value)
305 }
306
307 pub fn attribute_float(&mut self, name: &str, value: f32) -> Result<(), ConversionError> {
308 self.output.write_byte(Self::ATTRIBUTE | Self::TYPE_FLOAT)?;
309 self.output.write_interned_utf(name)?;
310 self.output.write_float(value)
311 }
312
313 pub fn attribute_double(&mut self, name: &str, value: f64) -> Result<(), ConversionError> {
314 self.output
315 .write_byte(Self::ATTRIBUTE | Self::TYPE_DOUBLE)?;
316 self.output.write_interned_utf(name)?;
317 self.output.write_double(value)
318 }
319
320 pub fn attribute_boolean(&mut self, name: &str, value: bool) -> Result<(), ConversionError> {
321 let token = if value {
322 Self::ATTRIBUTE | Self::TYPE_BOOLEAN_TRUE
323 } else {
324 Self::ATTRIBUTE | Self::TYPE_BOOLEAN_FALSE
325 };
326 self.output.write_byte(token)?;
327 self.output.write_interned_utf(name)
328 }
329
330 pub fn text(&mut self, text: &str) -> Result<(), ConversionError> {
331 self.write_token(Self::TEXT, Some(text))
332 }
333
334 pub fn cdsect(&mut self, text: &str) -> Result<(), ConversionError> {
335 self.write_token(Self::CDSECT, Some(text))
336 }
337
338 pub fn comment(&mut self, text: &str) -> Result<(), ConversionError> {
339 self.write_token(Self::COMMENT, Some(text))
340 }
341
342 pub fn processing_instruction(
343 &mut self,
344 target: &str,
345 data: Option<&str>,
346 ) -> Result<(), ConversionError> {
347 let full_pi = if let Some(data) = data {
348 if data.is_empty() {
349 target.to_string()
350 } else {
351 format!("{} {}", target, data)
352 }
353 } else {
354 target.to_string()
355 };
356 self.write_token(Self::PROCESSING_INSTRUCTION, Some(&full_pi))
357 }
358
359 pub fn docdecl(&mut self, text: &str) -> Result<(), ConversionError> {
360 self.write_token(Self::DOCDECL, Some(text))
361 }
362
363 pub fn ignorable_whitespace(&mut self, text: &str) -> Result<(), ConversionError> {
364 self.write_token(Self::IGNORABLE_WHITESPACE, Some(text))
365 }
366
367 pub fn entity_ref(&mut self, text: &str) -> Result<(), ConversionError> {
368 self.write_token(Self::ENTITY_REF, Some(text))
369 }
370}
371
372mod type_detection {
373 pub fn is_numeric(s: &str) -> bool {
374 if s.is_empty() {
375 return false;
376 }
377 let start = if s.starts_with('-') { 1 } else { 0 };
378 s.chars().skip(start).all(|c| c.is_ascii_digit())
379 }
380
381 pub fn is_hex_number(s: &str) -> bool {
382 if s.len() < 3 {
383 return false;
384 }
385 let lower = s.to_lowercase();
386 if !lower.starts_with("0x") {
387 return false;
388 }
389 s.chars().skip(2).all(|c| c.is_ascii_hexdigit())
390 }
391
392 pub fn is_float(s: &str) -> bool {
393 if s.is_empty() {
394 return false;
395 }
396 let mut has_dot = false;
397 let start = if s.starts_with('-') { 1 } else { 0 };
398
399 for c in s.chars().skip(start) {
400 if c == '.' {
401 if has_dot {
402 return false;
403 }
404 has_dot = true;
405 } else if !c.is_ascii_digit() {
406 return false;
407 }
408 }
409 has_dot
410 }
411
412 pub fn is_double(s: &str) -> bool {
413 if s.contains('e') || s.contains('E') {
414 return true;
415 }
416 if is_float(s) && s.len() > 10 {
417 return true;
418 }
419 false
420 }
421
422 pub fn is_boolean(s: &str) -> bool {
423 s == "true" || s == "false"
424 }
425
426 pub fn is_base64(s: &str) -> bool {
427 if s.is_empty() || s.len() % 4 != 0 {
428 return false;
429 }
430 s.chars()
431 .all(|c| c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '=')
432 }
433
434 pub fn is_hex_string(s: &str) -> bool {
435 if s.len() % 2 != 0 {
436 return false;
437 }
438 s.chars().all(|c| c.is_ascii_hexdigit())
439 }
440
441 pub fn is_whitespace_only(s: &str) -> bool {
442 s.chars().all(|c| c.is_whitespace())
443 }
444}
445
446pub struct XmlToAbxConverter;
447
448impl XmlToAbxConverter {
449 pub fn convert_from_string<W: Write>(xml: &str, writer: W) -> Result<(), ConversionError> {
450 Self::convert_from_string_with_options(xml, writer, true)
451 }
452
453 pub fn convert_from_string_with_options<W: Write>(
454 xml: &str,
455 writer: W,
456 preserve_whitespace: bool,
457 ) -> Result<(), ConversionError> {
458 let mut reader = Reader::from_str(xml);
459 reader.config_mut().trim_text(!preserve_whitespace);
460 Self::convert_reader_with_options(reader, writer, preserve_whitespace)
461 }
462
463 pub fn convert_from_file<W: Write>(input_path: &str, writer: W) -> Result<(), ConversionError> {
464 Self::convert_from_file_with_options(input_path, writer, true)
465 }
466
467 pub fn convert_from_file_with_options<W: Write>(
468 input_path: &str,
469 writer: W,
470 preserve_whitespace: bool,
471 ) -> Result<(), ConversionError> {
472 let mut reader = Reader::from_file(input_path)?;
473 reader.config_mut().trim_text(!preserve_whitespace);
474 Self::convert_reader_with_options(reader, writer, preserve_whitespace)
475 }
476
477 pub fn convert_from_reader<R: BufRead, W: Write>(
478 input: R,
479 writer: W,
480 ) -> Result<(), ConversionError> {
481 Self::convert_from_reader_with_options(input, writer, true)
482 }
483
484 pub fn convert_from_reader_with_options<R: BufRead, W: Write>(
485 input: R,
486 writer: W,
487 preserve_whitespace: bool,
488 ) -> Result<(), ConversionError> {
489 let mut reader = Reader::from_reader(input);
490 reader.config_mut().trim_text(!preserve_whitespace);
491 Self::convert_reader_with_options(reader, writer, preserve_whitespace)
492 }
493
494 fn convert_reader_with_options<R: BufRead, W: Write>(
502 mut reader: Reader<R>,
503 writer: W,
504 preserve_whitespace: bool,
505 ) -> Result<(), ConversionError> {
506 let mut serializer = BinaryXmlSerializer::with_options(writer, preserve_whitespace)?;
507 let mut buf = Vec::new();
508 let mut tag_stack = Vec::new();
509
510 serializer.start_document()?;
511
512 loop {
513 match reader.read_event_into(&mut buf)? {
514 Event::Start(e) => {
515 let name_bytes = e.name();
516 let name = std::str::from_utf8(name_bytes.as_ref())?;
517 if name.contains(':') {
518 show_warning(
519 "Namespaces and prefixes",
520 Some(&format!("Found prefixed element: {}", name)),
521 );
522 }
523
524 serializer.start_tag(name)?;
525 tag_stack.push(name.to_string());
526 for attr in e.attributes() {
527 let attr = attr?;
528 let attr_name = std::str::from_utf8(attr.key.as_ref())?;
529 let attr_value = std::str::from_utf8(&attr.value)?;
530 if attr_name.starts_with("xmlns") || attr_name.contains(':') {
531 show_warning(
532 "Namespaces and prefixes",
533 Some(&format!(
534 "Found namespace declaration or prefixed attribute: {}",
535 attr_name
536 )),
537 );
538 }
539
540 Self::write_attribute(&mut serializer, attr_name, attr_value)?;
541 }
542 }
543 Event::End(e) => {
544 let name_bytes = e.name();
545 let name = std::str::from_utf8(name_bytes.as_ref())?;
546 serializer.end_tag(name)?;
547 tag_stack.pop();
548 }
549 Event::Empty(e) => {
550 let name_bytes = e.name();
551 let name = std::str::from_utf8(name_bytes.as_ref())?;
552 if name.contains(':') {
553 show_warning(
554 "Namespaces and prefixes",
555 Some(&format!("Found prefixed element: {}", name)),
556 );
557 }
558
559 serializer.start_tag(name)?;
560 for attr in e.attributes() {
561 let attr = attr?;
562 let attr_name = std::str::from_utf8(attr.key.as_ref())?;
563 let attr_value = std::str::from_utf8(&attr.value)?;
564 if attr_name.starts_with("xmlns") || attr_name.contains(':') {
565 show_warning(
566 "Namespaces and prefixes",
567 Some(&format!(
568 "Found namespace declaration or prefixed attribute: {}",
569 attr_name
570 )),
571 );
572 }
573
574 Self::write_attribute(&mut serializer, attr_name, attr_value)?;
575 }
576
577 serializer.end_tag(name)?;
578 }
579 Event::Text(e) => {
580 let text = std::str::from_utf8(&e)?;
581 if type_detection::is_whitespace_only(text) {
582 if serializer.preserve_whitespace {
583 serializer.ignorable_whitespace(text)?;
584 }
585 } else {
587 serializer.text(text)?;
588 }
589 }
590 Event::CData(e) => {
591 let text = std::str::from_utf8(&e)?;
592 serializer.cdsect(text)?;
593 }
594 Event::Comment(e) => {
595 let text = std::str::from_utf8(&e)?;
596 serializer.comment(text)?;
597 }
598 Event::PI(e) => {
599 let target = std::str::from_utf8(e.target())?;
600 let raw = e.content();
601 let data = if raw.is_empty() {
602 None
603 } else {
604 Some(std::str::from_utf8(raw)?)
605 };
606
607 if target == "xml" {
608 if let Some(content) = data {
609 if content.contains("encoding")
610 && !content.to_lowercase().contains("utf-8")
611 {
612 show_warning(
613 "Non‑UTF‑8 encoding",
614 Some(&format!("Found in declaration: {}", content)),
615 );
616 }
617 }
618 }
619
620 serializer.processing_instruction(target, data)?;
621 }
622 Event::Decl(decl) => {
623 if let Some(enc_result) = decl.encoding() {
624 let enc_bytes = enc_result?;
625 let enc = std::str::from_utf8(enc_bytes.as_ref())?;
626 if !enc.to_lowercase().contains("utf-8") {
627 show_warning(
628 "Non-UTF-8 encoding",
629 Some(&format!("Found encoding: {}", enc)),
630 );
631 }
632 }
633 }
634 Event::DocType(e) => {
635 let text = std::str::from_utf8(&e)?;
636 serializer.docdecl(text)?;
637 }
638 Event::GeneralRef(e) => {
639 let text = std::str::from_utf8(&e)?;
640 serializer.entity_ref(text)?;
641 }
642 Event::Eof => break,
643 }
644 buf.clear();
645 }
646
647 serializer.end_document()?;
648 Ok(())
649 }
650
651 fn write_attribute<W: Write>(
652 serializer: &mut BinaryXmlSerializer<W>,
653 name: &str,
654 value: &str,
655 ) -> Result<(), ConversionError> {
656 use type_detection::*;
657
658 if is_boolean(value) {
659 serializer.attribute_boolean(name, value == "true")?;
660 } else if is_hex_number(value) {
661 match Self::parse_hex_number(value) {
662 Ok((int_val, is_long)) => {
663 if is_long {
664 serializer.attribute_long_hex(name, int_val)?;
665 } else {
666 serializer.attribute_int_hex(name, int_val as i32)?;
667 }
668 }
669 Err(_) => {
670 serializer.attribute(name, value)?;
671 }
672 }
673 } else if is_numeric(value) {
674 match value.parse::<i32>() {
675 Ok(int_val) => {
676 serializer.attribute_int(name, int_val)?;
677 }
678 Err(_) => match value.parse::<i64>() {
679 Ok(long_val) => {
680 serializer.attribute_long(name, long_val)?;
681 }
682 Err(_) => {
683 serializer.attribute(name, value)?;
684 }
685 },
686 }
687 } else if is_double(value) {
688 match value.parse::<f64>() {
689 Ok(double_val) => {
690 serializer.attribute_double(name, double_val)?;
691 }
692 Err(_) => {
693 serializer.attribute(name, value)?;
694 }
695 }
696 } else if is_float(value) {
697 match value.parse::<f32>() {
698 Ok(float_val) => {
699 serializer.attribute_float(name, float_val)?;
700 }
701 Err(_) => {
702 serializer.attribute(name, value)?;
703 }
704 }
705 } else if is_base64(value) && value.len() > 8 {
706 match base64::engine::general_purpose::STANDARD.decode(value) {
707 Ok(decoded) => {
708 serializer.attribute_bytes_base64(name, &decoded)?;
709 }
710 Err(_) => {
711 serializer.attribute(name, value)?;
712 }
713 }
714 } else if is_hex_string(value) && value.len() > 2 {
715 match hex::decode(value) {
716 Ok(decoded) => {
717 serializer.attribute_bytes_hex(name, &decoded)?;
718 }
719 Err(_) => {
720 serializer.attribute(name, value)?;
721 }
722 }
723 } else {
724 if value.len() < 50
725 && (!value.contains(' ')
726 || value == "true"
727 || value == "false"
728 || value.contains('.'))
729 {
730 serializer.attribute_interned(name, value)?;
731 } else {
732 serializer.attribute(name, value)?;
733 }
734 }
735 Ok(())
736 }
737
738 fn parse_hex_number(s: &str) -> Result<(i64, bool), ConversionError> {
739 let hex_part = &s[2..]; if hex_part.len() <= 8 {
741 i64::from_str_radix(hex_part, 16)
742 .map(|val| (val, false))
743 .map_err(|_| ConversionError::InvalidHex)
744 } else {
745 i64::from_str_radix(hex_part, 16)
746 .map(|val| (val, true))
747 .map_err(|_| ConversionError::InvalidHex)
748 }
749 }
750}