mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	perf(vim.text): use lookup table implementation for hex encoding (#30080)
Co-authored-by: glepnir <glephunter@gmail.com>
This commit is contained in:
		| @@ -2,6 +2,18 @@ | ||||
|  | ||||
| local M = {} | ||||
|  | ||||
| local alphabet = '0123456789ABCDEF' | ||||
| local atoi = {} ---@type table<string, integer> | ||||
| local itoa = {} ---@type table<integer, string> | ||||
| do | ||||
|   for i = 1, #alphabet do | ||||
|     local char = alphabet:sub(i, i) | ||||
|     itoa[i - 1] = char | ||||
|     atoi[char] = i - 1 | ||||
|     atoi[char:lower()] = i - 1 | ||||
|   end | ||||
| end | ||||
|  | ||||
| --- Hex encode a string. | ||||
| --- | ||||
| --- @param str string String to encode | ||||
| @@ -9,7 +21,9 @@ local M = {} | ||||
| function M.hexencode(str) | ||||
|   local enc = {} ---@type string[] | ||||
|   for i = 1, #str do | ||||
|     enc[i] = string.format('%02X', str:byte(i, i + 1)) | ||||
|     local byte = str:byte(i) | ||||
|     enc[2 * i - 1] = itoa[math.floor(byte / 16)] | ||||
|     enc[2 * i] = itoa[byte % 16] | ||||
|   end | ||||
|   return table.concat(enc) | ||||
| end | ||||
| @@ -26,8 +40,12 @@ function M.hexdecode(enc) | ||||
|  | ||||
|   local str = {} ---@type string[] | ||||
|   for i = 1, #enc, 2 do | ||||
|     local n = assert(tonumber(enc:sub(i, i + 1), 16)) | ||||
|     str[#str + 1] = string.char(n) | ||||
|     local u = atoi[enc:sub(i, i)] | ||||
|     local l = atoi[enc:sub(i + 1, i + 1)] | ||||
|     if not u or not l then | ||||
|       return nil, 'string must contain only hex characters' | ||||
|     end | ||||
|     str[(i + 1) / 2] = string.char(u * 16 + l) | ||||
|   end | ||||
|   return table.concat(str), nil | ||||
| end | ||||
|   | ||||
							
								
								
									
										52
									
								
								test/benchmark/text_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								test/benchmark/text_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| describe('vim.text', function() | ||||
|   --- @param t number[] | ||||
|   local function mean(t) | ||||
|     assert(#t > 0) | ||||
|     local sum = 0 | ||||
|     for _, v in ipairs(t) do | ||||
|       sum = sum + v | ||||
|     end | ||||
|     return sum / #t | ||||
|   end | ||||
|  | ||||
|   --- @param t number[] | ||||
|   local function median(t) | ||||
|     local len = #t | ||||
|     if len % 2 == 0 then | ||||
|       return t[len / 2] | ||||
|     end | ||||
|     return t[(len + 1) / 2] | ||||
|   end | ||||
|  | ||||
|   --- @param f fun(t: number[]): table<number, number|string|table> | ||||
|   local function measure(f, input, N) | ||||
|     local stats = {} ---@type number[] | ||||
|     for _ = 1, N do | ||||
|       local tic = vim.uv.hrtime() | ||||
|       f(input) | ||||
|       local toc = vim.uv.hrtime() | ||||
|       stats[#stats + 1] = (toc - tic) / 1000000 | ||||
|     end | ||||
|     table.sort(stats) | ||||
|     print( | ||||
|       string.format( | ||||
|         '\nN: %d, Min: %0.6f ms, Max: %0.6f ms, Median: %0.6f ms, Mean: %0.6f ms', | ||||
|         N, | ||||
|         math.min(unpack(stats)), | ||||
|         math.max(unpack(stats)), | ||||
|         median(stats), | ||||
|         mean(stats) | ||||
|       ) | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|   local input, output = string.rep('😂', 2 ^ 16), string.rep('F09F9882', 2 ^ 16) | ||||
|  | ||||
|   it('hexencode', function() | ||||
|     measure(vim.text.hexencode, input, 100) | ||||
|   end) | ||||
|  | ||||
|   it('hexdecode', function() | ||||
|     measure(vim.text.hexdecode, output, 100) | ||||
|   end) | ||||
| end) | ||||
| @@ -26,5 +26,21 @@ describe('vim.text', function() | ||||
|       eq(output, vim.text.hexencode(input)) | ||||
|       eq(input, vim.text.hexdecode(output)) | ||||
|     end) | ||||
|  | ||||
|     it('errors on invalid input', function() | ||||
|       -- Odd number of hex characters | ||||
|       do | ||||
|         local res, err = vim.text.hexdecode('ABC') | ||||
|         eq(nil, res) | ||||
|         eq('string must have an even number of hex characters', err) | ||||
|       end | ||||
|  | ||||
|       -- Non-hexadecimal input | ||||
|       do | ||||
|         local res, err = vim.text.hexdecode('nothex') | ||||
|         eq(nil, res) | ||||
|         eq('string must contain only hex characters', err) | ||||
|       end | ||||
|     end) | ||||
|   end) | ||||
| end) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Gregory Anders
					Gregory Anders