rustpython_common/encodings/
raw_unicode_escape.rs1use super::*;
4use crate::wtf8::{CodePoint, Wtf8, Wtf8Buf};
5
6pub const ENCODING_NAME: &str = "rawunicodeescape";
7
8#[must_use]
11pub fn encode_bytes(s: &Wtf8) -> Vec<u8> {
12 let mut out = Vec::new();
13 for cp in s.code_points() {
14 let v = cp.to_u32();
15 if v < 0x100 {
16 out.push(v as u8);
17 } else if v < 0x10000 {
18 out.extend_from_slice(format!("\\u{v:04x}").as_bytes());
19 } else {
20 out.extend_from_slice(format!("\\U{v:08x}").as_bytes());
21 }
22 }
23 out
24}
25
26pub fn encode<Ctx, E>(ctx: Ctx, _errors: &E) -> Result<Vec<u8>, Ctx::Error>
27where
28 Ctx: EncodeContext,
29 E: EncodeErrorHandler<Ctx>,
30{
31 Ok(encode_bytes(ctx.remaining_data()))
32}
33
34pub fn decode<Ctx, E>(
35 mut ctx: Ctx,
36 errors: &E,
37 final_decode: bool,
38) -> Result<(Wtf8Buf, usize), Ctx::Error>
39where
40 Ctx: DecodeContext,
41 E: DecodeErrorHandler<Ctx>,
42{
43 let mut out = Wtf8Buf::new();
44 loop {
45 let rest = ctx.remaining_data();
46 if rest.is_empty() {
47 break;
48 }
49 let b = rest[0];
50 if b != b'\\' {
51 out.push_char(b as char);
52 ctx.advance(1);
53 continue;
54 }
55 let kind = rest.get(1).copied();
56 let want = match kind {
57 Some(b'u') => 4usize,
58 Some(b'U') => 8usize,
59 _ => 0,
60 };
61 if want != 0 {
62 let escape_start = ctx.position();
63 let digits_start = 2;
64 if !final_decode && rest.len() < digits_start + want {
65 break;
66 }
67 let available = (digits_start + want).min(rest.len());
68 let mut hex_end = digits_start;
69 while hex_end < available && rest[hex_end].is_ascii_hexdigit() {
70 hex_end += 1;
71 }
72 let numeric = if available == digits_start + want && hex_end == available {
73 core::str::from_utf8(&rest[digits_start..available])
74 .ok()
75 .and_then(|s| u32::from_str_radix(s, 16).ok())
76 } else {
77 None
78 };
79 if let Some(c) = numeric.and_then(CodePoint::from_u32) {
80 out.push(c);
81 ctx.advance(available);
82 continue;
83 }
84 let error_end = ctx.position()
85 + if numeric.is_some() {
86 available
87 } else {
88 hex_end
89 };
90 let reason = if numeric.is_some() {
91 "illegal Unicode character"
92 } else if want == 4 {
93 "truncated \\uXXXX escape"
94 } else {
95 "truncated \\UXXXXXXXX escape"
96 };
97 let replace = ctx.handle_error(errors, escape_start..error_end, Some(reason))?;
98 out.push_wtf8(replace.as_ref());
99 continue;
100 }
101 if kind.is_none() && !final_decode {
102 break;
103 }
104 out.push_char(b as char);
105 ctx.advance(1);
106 if let Some(next) = kind {
107 out.push_char(next as char);
108 ctx.advance(1);
109 }
110 }
111 Ok((out, ctx.position()))
112}