ansi8.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package graphemes
  2. // ansiEscapeLength8Bit returns the byte length of a valid 8-bit C1 ANSI
  3. // sequence at the start of data, or 0 if none.
  4. //
  5. // Recognized forms (ECMA-48 / ISO 6429):
  6. // - C1 CSI (0x9B) body as parameter/intermediate/final bytes
  7. // - C1 OSC (0x9D) body terminated by BEL, C1 ST, CAN, or SUB
  8. // - C1 DCS/SOS/PM/APC (0x90/0x98/0x9E/0x9F) body terminated by C1 ST, CAN, or SUB
  9. // - Standalone C1 controls (0x80..0x9F not listed above): single byte
  10. func ansiEscapeLength8Bit[T ~string | ~[]byte](data T) int {
  11. if len(data) == 0 {
  12. return 0
  13. }
  14. switch data[0] {
  15. case 0x9B: // C1 CSI
  16. body := csiBodyLength(data[1:])
  17. if body == 0 {
  18. return 0
  19. }
  20. return 1 + body
  21. case 0x9D: // C1 OSC
  22. body := oscLengthC1(data[1:])
  23. if body < 0 {
  24. return 0
  25. }
  26. return 1 + body
  27. case 0x90, 0x98, 0x9E, 0x9F: // C1 DCS, SOS, PM, APC
  28. body := stSequenceLengthC1(data[1:])
  29. if body < 0 {
  30. return 0
  31. }
  32. return 1 + body
  33. default:
  34. if data[0] >= 0x80 && data[0] <= 0x9F {
  35. return 1
  36. }
  37. }
  38. return 0
  39. }
  40. // oscLengthC1 returns the length of a C1 OSC body.
  41. // data is the slice after the C1 OSC initiator (0x9D).
  42. //
  43. // Returns:
  44. // - n >= 0: consumed body length (includes BEL/ST terminator when present)
  45. // - -1: not terminated in the provided data
  46. //
  47. // Terminators: BEL (0x07) or C1 ST (0x9C).
  48. // CAN (0x18) and SUB (0x1A) cancel the control string.
  49. func oscLengthC1[T ~string | ~[]byte](data T) int {
  50. for i := 0; i < len(data); i++ {
  51. b := data[i]
  52. if b == bel || b == st {
  53. return i + 1
  54. }
  55. if b == can || b == sub {
  56. return i
  57. }
  58. }
  59. return -1
  60. }
  61. // stSequenceLengthC1 parses DCS/SOS/PM/APC bodies that terminate with C1 ST
  62. // (0x9C), or are canceled by CAN/SUB.
  63. func stSequenceLengthC1[T ~string | ~[]byte](data T) int {
  64. for i := 0; i < len(data); i++ {
  65. b := data[i]
  66. if b == can || b == sub {
  67. return i
  68. }
  69. if b == st {
  70. return i + 1
  71. }
  72. }
  73. return -1
  74. }