Skip to content

Commit 7eee4e4

Browse files
author
skywind3000
committed
optimize with ffi (luajit builtin module).
1 parent edd71f7 commit 7eee4e4

File tree

2 files changed

+139
-15
lines changed

2 files changed

+139
-15
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ As you see, z.lua is the fastest one and requires less resource.
458458

459459
## History
460460

461+
- 1.6.0 (2019-03-04): optimize with ffi module (luajit builtin module).
461462
- 1.5.11 (2019-03-02): fixed: os.path.isdir doesn't work for symbol link folders.
462463
- 1.5.10 (2019-03-01): Prevent writing file racing.
463464
- 1.5.9 (2019-02-25): `z -b` should not match current directory (close #56).

z.lua

Lines changed: 138 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
-- z.lua - a cd command that learns, by skywind 2018, 2019
55
-- Licensed under MIT license.
66
--
7-
-- Version 1.5.11, Last Modified: 2019/03/02 11:37
7+
-- Version 1.6.0, Last Modified: 2019/03/04 14:47
88
--
99
-- * 10x faster than fasd and autojump, 3x faster than z.sh
1010
-- * available for posix shells: bash, zsh, sh, ash, dash, busybox
@@ -302,10 +302,107 @@ function os.log(text)
302302
end
303303

304304

305+
-----------------------------------------------------------------------
306+
-- ffi optimize (luajit has builtin ffi module)
307+
-----------------------------------------------------------------------
308+
os.native = {}
309+
os.native.status, os.native.ffi = pcall(require, "ffi")
310+
if os.native.status then
311+
local ffi = os.native.ffi
312+
if windows then
313+
ffi.cdef[[
314+
int GetFullPathNameA(const char *name, uint32_t size, char *out, char **name);
315+
int ReplaceFileA(const char *dstname, const char *srcname, void *, uint32_t, void *, void *);
316+
uint32_t GetTickCount(void);
317+
uint32_t GetFileAttributesA(const char *name);
318+
uint32_t GetCurrentDirectoryA(uint32_t size, char *ptr);
319+
]]
320+
local kernel32 = ffi.load('kernel32.dll')
321+
local buffer = ffi.new('char[?]', 300)
322+
local INVALID_FILE_ATTRIBUTES = 0xffffffff
323+
local FILE_ATTRIBUTE_DIRECTORY = 0x10
324+
os.native.kernel32 = kernel32
325+
function os.native.GetFullPathName(name)
326+
local hr = kernel32.GetFullPathNameA(name, 290, buffer, nil)
327+
return (hr > 0) and ffi.string(buffer, hr) or nil
328+
end
329+
function os.native.ReplaceFile(replaced, replacement)
330+
local hr = kernel32.ReplaceFileA(replaced, replacement, nil, 2, nil, nil)
331+
return (hr ~= 0) and true or false
332+
end
333+
function os.native.GetTickCount()
334+
return kernel32.GetTickCount()
335+
end
336+
function os.native.GetFileAttributes(name)
337+
return kernel32.GetFileAttributesA(name)
338+
end
339+
function os.native.exists(name)
340+
local attr = os.native.GetFileAttributes(name)
341+
return attr ~= INVALID_FILE_ATTRIBUTES
342+
end
343+
function os.native.isdir(name)
344+
local attr = os.native.GetFileAttributes(name)
345+
local isdir = FILE_ATTRIBUTE_DIRECTORY
346+
if attr == INVALID_FILE_ATTRIBUTES then
347+
return false
348+
end
349+
return (attr % (2 * isdir)) >= isdir
350+
end
351+
function os.native.getcwd()
352+
local hr = kernel32.GetCurrentDirectoryA(299, buffer)
353+
if hr <= 0 then return nil end
354+
return ffi.string(buffer, hr)
355+
end
356+
else
357+
ffi.cdef[[
358+
typedef struct { long tv_sec; long tv_usec; } timeval;
359+
int gettimeofday(timeval *tv, void *tz);
360+
int access(const char *name, int mode);
361+
char *realpath(const char *path, char *resolve);
362+
char *getcwd(char *buf, size_t size);
363+
]]
364+
local timeval = ffi.new('timeval[?]', 1)
365+
local buffer = ffi.new('char[?]', 4100)
366+
function os.native.gettimeofday()
367+
local hr = ffi.C.gettimeofday(timeval, nil)
368+
local sec = tonumber(timeval[0].tv_sec)
369+
local usec = tonumber(timeval[0].tv_usec)
370+
return sec + (usec * 0.000001)
371+
end
372+
function os.native.access(name, mode)
373+
return ffi.C.access(name, mode)
374+
end
375+
function os.native.realpath(name)
376+
local path = ffi.C.realpath(name, buffer)
377+
return (path ~= nil) and ffi.string(buffer) or nil
378+
end
379+
function os.native.getcwd()
380+
local hr = ffi.C.getcwd(buffer, 4099)
381+
return hr ~= nil and ffi.string(buffer) or nil
382+
end
383+
end
384+
function os.native.tickcount()
385+
if windows then
386+
return os.native.GetTickCount()
387+
else
388+
return math.floor(os.native.gettimeofday() * 1000)
389+
end
390+
end
391+
os.native.init = true
392+
end
393+
394+
305395
-----------------------------------------------------------------------
306396
-- get current path
307397
-----------------------------------------------------------------------
308398
function os.pwd()
399+
if os.native and os.native.getcwd then
400+
local hr = os.native.getcwd()
401+
if hr then return hr end
402+
end
403+
if os.getcwd then
404+
return os.getcwd()
405+
end
309406
if windows then
310407
local fp = io.popen('cd')
311408
if fp == nil then
@@ -372,6 +469,10 @@ end
372469
-----------------------------------------------------------------------
373470
function os.path.abspath(path)
374471
if path == '' then path = '.' end
472+
if os.native and os.native.GetFullPathName then
473+
local test = os.native.GetFullPathName(path)
474+
if test then return test end
475+
end
375476
if windows then
376477
local script = 'FOR /f "delims=" %%i IN ("%s") DO @echo %%~fi'
377478
local script = string.format(script, path)
@@ -429,9 +530,15 @@ function os.path.isdir(pathname)
429530
return true
430531
end
431532
end
533+
if os.native and os.native.isdir then
534+
return os.native.isdir(pathname)
535+
end
536+
if clink and os.isdir then
537+
return os.isdir(pathname)
538+
end
432539
local name = pathname
433540
if (not name:endswith('/')) and (not name:endswith('\\')) then
434-
name = name .. '/'
541+
name = name .. os.path.sep
435542
end
436543
return os.path.exists(name)
437544
end
@@ -871,6 +978,9 @@ function math.random_init()
871978
seed = seed .. rnd
872979
end
873980
seed = seed .. tostring(os.clock() * 10000000)
981+
if os.native and os.native.tickcount then
982+
seed = seed .. tostring(os.native.tickcount())
983+
end
874984
local number = 0
875985
for i = 1, seed:len() do
876986
local k = string.byte(seed:sub(i, i))
@@ -960,20 +1070,29 @@ function data_save(filename, M)
9601070
local tmpname = nil
9611071
local i
9621072
filename = os.path.expand(filename)
963-
if windows then
964-
fp = io.open(filename, 'w')
965-
else
966-
math.random_init()
967-
while true do
968-
tmpname = filename .. '.' .. tostring(os.time())
1073+
math.random_init()
1074+
while true do
1075+
tmpname = filename .. '.' .. tostring(os.time())
1076+
if os.native and os.native.tickcount then
1077+
local key = os.native.tickcount() % 1000
1078+
tmpname = tmpname .. string.format('%03d', key)
1079+
tmpname = tmpname .. math.random_string(5)
1080+
else
9691081
tmpname = tmpname .. math.random_string(8)
970-
local rnd = os.getenv('_ZL_RANDOM')
971-
tmpname = tmpname .. '' .. (rnd and rnd or '')
972-
if not os.path.exists(tmpname) then
973-
-- print('tmpname: '..tmpname)
974-
break
975-
end
9761082
end
1083+
if not os.path.exists(tmpname) then
1084+
-- print('tmpname: '..tmpname)
1085+
break
1086+
end
1087+
end
1088+
if windows then
1089+
if os.native and os.native.ReplaceFile then
1090+
fp = io.open(tmpname, 'w')
1091+
else
1092+
fp = io.open(filename, 'w')
1093+
tmpname = nil
1094+
end
1095+
else
9771096
fp = io.open(tmpname, 'w')
9781097
end
9791098
if fp == nil then
@@ -986,7 +1105,11 @@ function data_save(filename, M)
9861105
end
9871106
fp:close()
9881107
if tmpname ~= nil then
989-
os.rename(tmpname, filename)
1108+
if windows then
1109+
os.native.ReplaceFile(filename, tmpname)
1110+
else
1111+
os.rename(tmpname, filename)
1112+
end
9901113
os.remove(tmpname)
9911114
end
9921115
return true

0 commit comments

Comments
 (0)