src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

ansi8.go (1904B)


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