1- """Encode valid C string literals from Python strings.
2-
3- If a character is not allowed in C string literals, it is either emitted
4- as a simple escape sequence (e.g. '\\ n'), or an octal escape sequence
5- with exactly three digits ('\\ oXXX'). Question marks are escaped to
6- prevent trigraphs in the string literal from being interpreted. Note
7- that '\\ ?' is an invalid escape sequence in Python.
8-
9- Consider the string literal "AB\\ xCDEF". As one would expect, Python
10- parses it as ['A', 'B', 0xCD, 'E', 'F']. However, the C standard
11- specifies that all hexadecimal digits immediately following '\\ x' will
12- be interpreted as part of the escape sequence. Therefore, it is
13- unexpectedly parsed as ['A', 'B', 0xCDEF].
14-
15- Emitting ("AB\\ xCD" "EF") would avoid this behaviour. However, we opt
16- for simplicity and use octal escape sequences instead. They do not
17- suffer from the same issue as they are defined to parse at most three
18- octal digits.
19- """
1+ """Utilities for generating C string literals."""
202
213from __future__ import annotations
224
23- import string
245from typing import Final
256
26- CHAR_MAP : Final = [ f" \\ { i :03o } " for i in range ( 256 )]
7+ _TRANSLATION_TABLE : Final [ dict [ int , str ]] = {}
278
28- # It is safe to use string.printable as it always uses the C locale.
29- for c in string .printable :
30- CHAR_MAP [ord (c )] = c
319
32- # These assignments must come last because we prioritize simple escape
33- # sequences over any other representation.
34- for c in ("'" , '"' , "\\ " , "a" , "b" , "f" , "n" , "r" , "t" , "v" ):
35- escaped = f"\\ { c } "
36- decoded = escaped .encode ("ascii" ).decode ("unicode_escape" )
37- CHAR_MAP [ord (decoded )] = escaped
10+ def _init_translation_table () -> None :
11+ for i in range (256 ):
12+ if i == ord ("\n " ):
13+ s = "\\ n"
14+ elif i == ord ("\r " ):
15+ s = "\\ r"
16+ elif i == ord ("\t " ):
17+ s = "\\ t"
18+ elif i == ord ('"' ):
19+ s = '\\ "'
20+ elif i == ord ("\\ " ):
21+ s = "\\ \\ "
22+ elif 32 <= i < 127 :
23+ s = chr (i )
24+ else :
25+ s = "\\ x%02x" % i
26+ _TRANSLATION_TABLE [i ] = s
3827
39- # This escape sequence is invalid in Python.
40- CHAR_MAP [ord ("?" )] = r"\?"
4128
42-
43- def encode_bytes_as_c_string (b : bytes ) -> str :
44- """Produce contents of a C string literal for a byte string, without quotes."""
45- escaped = "" .join ([CHAR_MAP [i ] for i in b ])
46- return escaped
29+ _init_translation_table ()
4730
4831
4932def c_string_initializer (value : bytes ) -> str :
50- """Create initializer for a C char[]/ char * variable from a string .
33+ """Convert a bytes object to a C string literal initializer .
5134
52- For example, if value if b'foo', the result would be ' "foo"'.
35+ Returns a string like ' "foo\\ nbar "'.
5336 """
54- return '"' + encode_bytes_as_c_string ( value ) + '"'
37+ return '"' + value . decode ( "latin1" ). translate ( _TRANSLATION_TABLE ) + '"'
0 commit comments