gen_rfc8259_embedded.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env python3
  2. import os
  3. import re
  4. from pathlib import Path
  5. def repo_root_from_script() -> Path:
  6. return Path(__file__).resolve().parents[2]
  7. def escape_c_byte(b: int) -> str:
  8. if b == 0x22:
  9. return r"\""
  10. if b == 0x5C:
  11. return r"\\"
  12. if b == 0x0A:
  13. return r"\n"
  14. if b == 0x0D:
  15. return r"\r"
  16. if b == 0x09:
  17. return r"\t"
  18. if b == 0x08:
  19. return r"\b"
  20. if b == 0x0C:
  21. return r"\f"
  22. if 0x20 <= b <= 0x7E:
  23. return chr(b)
  24. return f"\\{b:03o}"
  25. def format_c_string(data: bytes, indent: str = " ", max_line_len: int = 120) -> str:
  26. if data is None:
  27. return indent + "\"\"\n"
  28. lines = []
  29. current = indent + "\""
  30. for b in data:
  31. chunk = escape_c_byte(b)
  32. if len(current) + len(chunk) >= max_line_len:
  33. current += "\"\n"
  34. lines.append(current)
  35. current = indent + "\"" + chunk
  36. else:
  37. current += chunk
  38. current += "\"\n"
  39. lines.append(current)
  40. return "".join(lines)
  41. def main() -> None:
  42. repo_root = repo_root_from_script()
  43. data_dir = repo_root / "test" / "data" / "rfc8259"
  44. out_dir = repo_root / "test" / "unityTest" / "cases" / "RFC8259"
  45. out_c = out_dir / "rfc8259Embedded.c"
  46. out_h = out_dir / "rfc8259Embedded.h"
  47. names = sorted([p.name for p in data_dir.iterdir() if p.is_file() and p.name.endswith(".json")])
  48. if not names:
  49. raise SystemExit("No RFC8259 json files found.")
  50. out_dir.mkdir(parents=True, exist_ok=True)
  51. # Header
  52. header = []
  53. header.append("#ifndef RYANJSON_RFC8259_EMBEDDED_H\n")
  54. header.append("#define RYANJSON_RFC8259_EMBEDDED_H\n\n")
  55. header.append("#include <stdint.h>\n\n")
  56. header.append("typedef struct\n{")
  57. header.append("\tconst char *name;\n")
  58. header.append("\tconst unsigned char *data;\n")
  59. header.append("\tuint32_t len;\n")
  60. header.append("} rfc8259EmbeddedFile_t;\n\n")
  61. header.append("extern const rfc8259EmbeddedFile_t gRfc8259EmbeddedFiles[];\n")
  62. header.append("extern const uint32_t gRfc8259EmbeddedFileCount;\n\n")
  63. header.append("#endif // RYANJSON_RFC8259_EMBEDDED_H\n")
  64. out_h.write_text("".join(header), encoding="utf-8")
  65. # Source
  66. src = []
  67. src.append("// Auto-generated by scripts/tools/gen_rfc8259_embedded.py. Do not edit by hand.\n")
  68. src.append("#include \"rfc8259Embedded.h\"\n\n")
  69. entries = []
  70. for idx, name in enumerate(names):
  71. path = data_dir / name
  72. data = path.read_bytes()
  73. stem = Path(name).stem
  74. parts = [p for p in re.split(r"[^a-zA-Z0-9]+", stem) if p]
  75. if not parts:
  76. camel = f"case{idx:03d}"
  77. else:
  78. first = parts[0].lower()
  79. rest = "".join(p[:1].upper() + p[1:] for p in parts[1:])
  80. camel = first + rest
  81. var_name = f"rfc8259Embedded{idx:03d}{camel}Json"
  82. src.append(f"static const unsigned char {var_name}[] =\n")
  83. src.append(format_c_string(data))
  84. src.append(";\n\n")
  85. entries.append((name, var_name, len(data)))
  86. src.append("const rfc8259EmbeddedFile_t gRfc8259EmbeddedFiles[] = {\n")
  87. for name, var_name, length in entries:
  88. src.append(f"\t{{\"{name}\", {var_name}, {length}U}},\n")
  89. src.append("};\n\n")
  90. src.append("const uint32_t gRfc8259EmbeddedFileCount = ")
  91. src.append("(uint32_t)(sizeof(gRfc8259EmbeddedFiles) / sizeof(gRfc8259EmbeddedFiles[0]));\n")
  92. out_c.write_text("".join(src), encoding="utf-8")
  93. if __name__ == "__main__":
  94. main()