@@ -256,6 +256,18 @@ for b = 97, 122 do hexlike_passthrough[b] = true end -- a-z
256256-- Οχ prefix: Greek Omicron (U+039F) + Greek Chi (U+03C7) — NOT ASCII "0x"
257257local OX = " \xCE\x9F\xCF\x87 "
258258
259+ -- Precomputed hexlike lookup tables (built once at load) so the per-byte hot
260+ -- loops touch a table instead of string.format / tonumber+match per byte.
261+ -- HEX2[b] -> the two-char uppercase hex string for byte b (encode side).
262+ -- HEXVAL[c] -> the nibble value (0-15) for an ASCII hex-digit byte c, else nil
263+ -- (decode side; lets us combine two nibbles with no concat/regex).
264+ local HEX2 = {}
265+ for b = 0 , 255 do HEX2 [b ] = string.format (" %02X" , b ) end
266+ local HEXVAL = {}
267+ for c = 0 , 9 do HEXVAL [48 + c ] = c end -- '0'-'9'
268+ for c = 0 , 5 do HEXVAL [65 + c ] = 10 + c end -- 'A'-'F'
269+ for c = 0 , 5 do HEXVAL [97 + c ] = 10 + c end -- 'a'-'f'
270+
259271-- Detect Οχ hex sequences in input (for cross-format warnings)
260272function PrintableBinary .detect_hexlike (input_string )
261273 if type (input_string ) ~= " string" or # input_string == 0 then
@@ -278,42 +290,43 @@ function PrintableBinary.hexlike_encode(binary_data, options)
278290 for k , v in pairs (hexlike_passthrough ) do pt [k ] = v end
279291 if spaces_mode then pt [32 ] = true end
280292
281- local result = {}
293+ -- One growable buffer instead of an accumulator table + final table.concat.
294+ -- Hex output is ~3x the input at most (2 hex chars + delimiters per byte),
295+ -- so size the buffer generously to avoid mid-loop growth.
296+ local sbuf = buffer .new (# binary_data * 3 )
282297 local i = 1
283298 local len = # binary_data
299+ local wrote = false -- tracks "have we emitted anything yet" (was #result > 0)
284300
285301 while i <= len do
286302 local byte = string.byte (binary_data , i )
287303
288304 if pt [byte ] then
289- -- Passthrough run
305+ -- Passthrough run: copy the whole run as one slice
290306 local run_start = i
291307 while i <= len and pt [string.byte (binary_data , i )] do
292308 i = i + 1
293309 end
294- result [# result + 1 ] = string.sub (binary_data , run_start , i - 1 )
310+ sbuf :put (string.sub (binary_data , run_start , i - 1 ))
311+ wrote = true
295312 else
296- -- Non-passthrough run: collect hex
297- local hex_parts = {}
313+ -- Non-passthrough run: emit Οχ then per-byte hex via the precomputed LUT
314+ -- Delimiter space before Οχ (unless at start of output)
315+ if wrote then sbuf :put (" " ) end
316+ sbuf :put (OX )
298317 while i <= len and not pt [string.byte (binary_data , i )] do
299- hex_parts [ # hex_parts + 1 ] = string.format ( " %02X " , string. byte (binary_data , i ))
318+ sbuf : put ( HEX2 [ string.byte (binary_data , i )] )
300319 i = i + 1
301320 end
302-
303- -- Delimiter space before Οχ (unless at start of output)
304- if # result > 0 then
305- result [# result + 1 ] = " "
306- end
307- result [# result + 1 ] = OX
308- result [# result + 1 ] = table.concat (hex_parts )
321+ wrote = true
309322 -- Delimiter space after hex run (unless at end of output)
310323 if i <= len then
311- result [ # result + 1 ] = " "
324+ sbuf : put ( " " )
312325 end
313326 end
314327 end
315328
316- return table.concat ( result )
329+ return sbuf : tostring ( )
317330end
318331
319332-- Decode hexlike format back to binary
@@ -323,11 +336,21 @@ function PrintableBinary.hexlike_decode(printable_string, options)
323336 end
324337
325338 options = options or {}
326- local result = {}
327- local i = 1
328339 local len = # printable_string
329340 local found_hex = false
330341
342+ -- Decode shrinks (each hex pair -> 1 byte, passthrough char -> 1 byte, Οχ and
343+ -- spaces drop out), so output never exceeds the input length. Reserve once and
344+ -- write bytes straight into the buffer via FFI — no per-byte string.char alloc,
345+ -- no accumulator table, no final concat. The hex pairs combine two nibble
346+ -- lookups (HEXVAL) instead of substring + match + tonumber(h1..h2,16).
347+ local out_cap = len
348+ if out_cap < 1 then out_cap = 1 end
349+ local dbuf = buffer .new (out_cap )
350+ local dptr = dbuf :reserve (out_cap )
351+ local dn = 0
352+ local i = 1
353+
331354 while i <= len do
332355 -- Check for Οχ (possibly preceded by delimiter space)
333356 local at_ox = false
@@ -345,27 +368,30 @@ function PrintableBinary.hexlike_decode(printable_string, options)
345368 i = i + 4 -- skip past Οχ
346369 -- Read uppercase hex pairs
347370 while i + 1 <= len do
348- local h1 = printable_string :sub (i , i )
349- local h2 = printable_string :sub (i + 1 , i + 1 )
350- if h1 :match (" [0-9A-Fa-f]" ) and h2 :match (" [0-9A-Fa-f]" ) then
351- result [# result + 1 ] = string.char (tonumber (h1 .. h2 , 16 ))
371+ local n1 = HEXVAL [string.byte (printable_string , i )]
372+ local n2 = HEXVAL [string.byte (printable_string , i + 1 )]
373+ if n1 and n2 then
374+ dptr [dn ] = n1 * 16 + n2
375+ dn = dn + 1
352376 i = i + 2
353377 else
354378 break
355379 end
356380 end
357381 -- Consume trailing delimiter space (if present)
358- if i <= len and printable_string : sub ( i , i ) == " " then
382+ if i <= len and string.byte ( printable_string , i ) == 32 then
359383 i = i + 1
360384 end
361385 else
362386 -- Passthrough byte
363- result [# result + 1 ] = printable_string :sub (i , i )
387+ dptr [dn ] = string.byte (printable_string , i )
388+ dn = dn + 1
364389 i = i + 1
365390 end
366391 end
367392
368- return table.concat (result ), found_hex
393+ dbuf :commit (dn )
394+ return dbuf :tostring (), found_hex
369395end
370396
371397-- Encode binary data to printable UTF-8
0 commit comments