3838 */
3939public class EscapeHexCellWriteHandler implements CellWriteHandler {
4040
41+ // ASCII hex digits only. Not Character.digit(c, 16), which also accepts non-ASCII
42+ // digits such as U+0663 that OOXML never uses.
43+ private static final boolean [] HEX_TABLE = new boolean [128 ];
44+
45+ static {
46+ for (char c = '0' ; c <= '9' ; c ++) HEX_TABLE [c ] = true ;
47+ for (char c = 'A' ; c <= 'F' ; c ++) HEX_TABLE [c ] = true ;
48+ for (char c = 'a' ; c <= 'f' ; c ++) HEX_TABLE [c ] = true ;
49+ }
50+
51+ private static final String PREFIX = "_x" ;
52+ private static final String ESCAPED_PREFIX = "_x005F" + PREFIX ;
53+ private static final int PREFIX_LENGTH = PREFIX .length ();
54+ private static final int HEX_DIGIT_COUNT = 4 ;
55+
4156 @ Override
4257 public void afterCellDataConverted (
4358 WriteSheetHolder writeSheetHolder ,
@@ -57,101 +72,56 @@ public void afterCellDataConverted(
5772 }
5873 }
5974
60- // Static hex lookup table for O(1) character validation
61- private static final boolean [] HEX_TABLE = new boolean [128 ];
62-
63- static {
64- for (char c = '0' ; c <= '9' ; c ++) HEX_TABLE [c ] = true ;
65- for (char c = 'A' ; c <= 'F' ; c ++) HEX_TABLE [c ] = true ;
66- for (char c = 'a' ; c <= 'f' ; c ++) HEX_TABLE [c ] = true ;
67- }
68-
6975 /**
70- * Escapes hexadecimal-encoded strings with optimized performance Replaces _xHHHH_ with _x005F_xHHHH_ to prevent POI
71- * from decoding them
76+ * Replaces every _xHHHH_ sequence with _x005F_xHHHH_ to prevent POI from decoding them.
7277 */
7378 private String escapeHex (String originalString ) {
7479 int length = originalString .length ();
7580
76- // Fast path: if string is too short to contain pattern, return original
77- if (length < 7 ) {
78- return originalString ;
79- }
80-
81- // Fast path: search for first potential pattern
81+ // Lazily allocated: stays null (no allocation) when no valid pattern is found
82+ StringBuilder result = null ;
83+ int lastEnd = 0 ;
8284 int searchStart = 0 ;
8385 int patternIndex ;
84- while ((patternIndex = originalString .indexOf ("_x" , searchStart )) != -1 ) {
85- // Check if we have enough characters for full pattern
86- if (patternIndex + 6 >= length ) {
86+ while ((patternIndex = originalString .indexOf (PREFIX , searchStart )) != -1 ) {
87+ int hexStart = patternIndex + PREFIX_LENGTH ;
88+ int suffixIndex = hexStart + HEX_DIGIT_COUNT ;
89+ int patternEnd = suffixIndex + 1 ;
90+ // Too few characters left for a full pattern, and any later match has even fewer
91+ if (patternEnd > length ) {
8792 break ;
8893 }
8994
90- // Quick validation: check if it ends with '_' and has valid hex
91- if (originalString .charAt (patternIndex + 6 ) == '_' && isValidHexFast (originalString , patternIndex + 2 )) {
92-
93- // Found at least one pattern, proceed with full processing
94- return processWithPatterns (originalString , patternIndex );
95- }
96-
97- searchStart = patternIndex + 2 ;
98- }
99-
100- // No valid patterns found
101- return originalString ;
102- }
103-
104- /**
105- * Process string when we know it contains at least one valid pattern
106- */
107- private String processWithPatterns (String originalString , int firstPatternIndex ) {
108- int length = originalString .length ();
109- StringBuilder result = new StringBuilder (length + 64 ); // More generous pre-allocation
110- int lastEnd ;
111-
112- // Process the first known pattern
113- result .append (originalString , 0 , firstPatternIndex );
114- result .append ("_x005F_x" );
115- result .append (originalString , firstPatternIndex + 2 , firstPatternIndex + 6 );
116- result .append ('_' );
117- lastEnd = firstPatternIndex + 7 ;
118-
119- // Continue searching for more patterns
120- int searchStart = firstPatternIndex + 7 ;
121- int patternIndex ;
122- while ((patternIndex = originalString .indexOf ("_x" , searchStart )) != -1 ) {
123- if (patternIndex + 6 >= length ) {
124- break ;
125- }
126-
127- if (originalString .charAt (patternIndex + 6 ) == '_' && isValidHexFast (originalString , patternIndex + 2 )) {
128-
129- // Append content between patterns
95+ if (originalString .charAt (suffixIndex ) == '_' && isHexDigits (originalString , hexStart )) {
96+ if (result == null ) {
97+ result = new StringBuilder (length + 64 );
98+ }
99+ // Append content since the previous match, then the escaped pattern
130100 result .append (originalString , lastEnd , patternIndex );
131- // Append escaped pattern
132- result .append ("_x005F_x" );
133- result .append (originalString , patternIndex + 2 , patternIndex + 6 );
101+ result .append (ESCAPED_PREFIX );
102+ result .append (originalString , hexStart , suffixIndex );
134103 result .append ('_' );
135- lastEnd = patternIndex + 7 ;
136- searchStart = patternIndex + 7 ;
104+ lastEnd = patternEnd ;
105+ searchStart = patternEnd ;
137106 } else {
138- searchStart = patternIndex + 2 ;
107+ searchStart = hexStart ;
139108 }
140109 }
141110
142- // Append remaining content
143- if (lastEnd < length ) {
144- result . append ( originalString , lastEnd , length ) ;
111+ // No valid patterns found
112+ if (result == null ) {
113+ return originalString ;
145114 }
146115
116+ result .append (originalString , lastEnd , length );
147117 return result .toString ();
148118 }
149119
150120 /**
151- * Fast hex validation using lookup table - O(1) per character
121+ * Checks whether the four characters starting at {@code startIndex} are all ASCII hex digits.
152122 */
153- private static boolean isValidHexFast (String str , int startIndex ) {
154- for (int i = 0 ; i < 4 ; i ++) {
123+ private static boolean isHexDigits (String str , int startIndex ) {
124+ for (int i = 0 ; i < HEX_DIGIT_COUNT ; i ++) {
155125 char c = str .charAt (startIndex + i );
156126 if (c >= 128 || !HEX_TABLE [c ]) {
157127 return false ;
0 commit comments