|
@@ -9,7 +9,12 @@
|
|
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
|
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
|
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
|
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
|
<style type="text/css">
|
|
<style type="text/css">
|
|
|
|
+span.codemark { position:absolute; left: 16em; color: #4040c0; }
|
|
|
|
+span.mark { color: #4040c0; font-family: Courier New, Courier, monospace;
|
|
|
|
+ line-height: 1.1; }
|
|
|
|
+pre.mark { padding-left: 2em; }
|
|
table.idiomtable { line-height: 1.2; }
|
|
table.idiomtable { line-height: 1.2; }
|
|
|
|
+table.idiomtable tt { font-size: 100%; }
|
|
tr.idiomhead td { font-weight: bold; }
|
|
tr.idiomhead td { font-weight: bold; }
|
|
td.idiomc { width: 12em; }
|
|
td.idiomc { width: 12em; }
|
|
td.idiomlua { width: 14em; }
|
|
td.idiomlua { width: 14em; }
|
|
@@ -94,27 +99,46 @@ The following code explains how to access standard system functions.
|
|
We slowly print two lines of dots by sleeping for 10 milliseconds
|
|
We slowly print two lines of dots by sleeping for 10 milliseconds
|
|
after each dot:
|
|
after each dot:
|
|
</p>
|
|
</p>
|
|
-<pre class="code">
|
|
|
|
-local ffi = require("ffi")
|
|
|
|
-ffi.cdef[[ <span style="color:#f0f4ff;">//</span><span style="color:#4040c0;">①</span>
|
|
|
|
|
|
+<pre class="code mark">
|
|
|
|
+<span class="codemark">
|
|
|
|
+①
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+②
|
|
|
|
+③
|
|
|
|
+④
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+⑤
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+⑥</span>local ffi = require("ffi")
|
|
|
|
+ffi.cdef[[
|
|
<span style="color:#00a000;">void Sleep(int ms);
|
|
<span style="color:#00a000;">void Sleep(int ms);
|
|
int poll(struct pollfd *fds, unsigned long nfds, int timeout);</span>
|
|
int poll(struct pollfd *fds, unsigned long nfds, int timeout);</span>
|
|
]]
|
|
]]
|
|
|
|
|
|
local sleep
|
|
local sleep
|
|
-if ffi.os == "Windows" then <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">②</span>
|
|
|
|
- function sleep(s) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">③</span>
|
|
|
|
- ffi.C.Sleep(s*1000) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">④</span>
|
|
|
|
|
|
+if ffi.os == "Windows" then
|
|
|
|
+ function sleep(s)
|
|
|
|
+ ffi.C.Sleep(s*1000)
|
|
end
|
|
end
|
|
else
|
|
else
|
|
function sleep(s)
|
|
function sleep(s)
|
|
- ffi.C.poll(nil, 0, s*1000) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">⑤</span>
|
|
|
|
|
|
+ ffi.C.poll(nil, 0, s*1000)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
for i=1,160 do
|
|
for i=1,160 do
|
|
io.write("."); io.flush()
|
|
io.write("."); io.flush()
|
|
- sleep(0.01) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">⑥</span>
|
|
|
|
|
|
+ sleep(0.01)
|
|
end
|
|
end
|
|
io.write("\n")
|
|
io.write("\n")
|
|
</pre>
|
|
</pre>
|
|
@@ -122,14 +146,14 @@ io.write("\n")
|
|
Here's the step-by-step explanation:
|
|
Here's the step-by-step explanation:
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">①</span> This defines the
|
|
|
|
|
|
+<span class="mark">①</span> This defines the
|
|
C library functions we're going to use. The part inside the
|
|
C library functions we're going to use. The part inside the
|
|
double-brackets (in green) is just standard C syntax. You can
|
|
double-brackets (in green) is just standard C syntax. You can
|
|
usually get this info from the C header files or the
|
|
usually get this info from the C header files or the
|
|
documentation provided by each C library or C compiler.
|
|
documentation provided by each C library or C compiler.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">②</span> The difficulty we're
|
|
|
|
|
|
+<span class="mark">②</span> The difficulty we're
|
|
facing here, is that there are different standards to choose from.
|
|
facing here, is that there are different standards to choose from.
|
|
Windows has a simple <tt>Sleep()</tt> function. On other systems there
|
|
Windows has a simple <tt>Sleep()</tt> function. On other systems there
|
|
are a variety of functions available to achieve sub-second sleeps, but
|
|
are a variety of functions available to achieve sub-second sleeps, but
|
|
@@ -139,14 +163,14 @@ check for <tt>ffi.os</tt> makes sure we use the Windows-specific
|
|
function only on Windows systems.
|
|
function only on Windows systems.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">③</span> Here we're wrapping the
|
|
|
|
|
|
+<span class="mark">③</span> Here we're wrapping the
|
|
call to the C function in a Lua function. This isn't strictly
|
|
call to the C function in a Lua function. This isn't strictly
|
|
necessary, but it's helpful to deal with system-specific issues only
|
|
necessary, but it's helpful to deal with system-specific issues only
|
|
in one part of the code. The way we're wrapping it ensures the check
|
|
in one part of the code. The way we're wrapping it ensures the check
|
|
for the OS is only done during initialization and not for every call.
|
|
for the OS is only done during initialization and not for every call.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">④</span> A more subtle point is
|
|
|
|
|
|
+<span class="mark">④</span> A more subtle point is
|
|
that we defined our <tt>sleep()</tt> function (for the sake of this
|
|
that we defined our <tt>sleep()</tt> function (for the sake of this
|
|
example) as taking the number of seconds, but accepting fractional
|
|
example) as taking the number of seconds, but accepting fractional
|
|
seconds. Multiplying this by 1000 gets us milliseconds, but that still
|
|
seconds. Multiplying this by 1000 gets us milliseconds, but that still
|
|
@@ -165,7 +189,7 @@ FFI library automatically detects <tt>stdcall</tt> functions, so you
|
|
don't need to declare them as such.
|
|
don't need to declare them as such.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">⑤</span> The <tt>poll()</tt>
|
|
|
|
|
|
+<span class="mark">⑤</span> The <tt>poll()</tt>
|
|
function takes a couple more arguments we're not going to use. You can
|
|
function takes a couple more arguments we're not going to use. You can
|
|
simply use <tt>nil</tt> to pass a <tt>NULL</tt> pointer and <tt>0</tt>
|
|
simply use <tt>nil</tt> to pass a <tt>NULL</tt> pointer and <tt>0</tt>
|
|
for the <tt>nfds</tt> parameter. Please note that the
|
|
for the <tt>nfds</tt> parameter. Please note that the
|
|
@@ -182,7 +206,7 @@ with this, as it's performed automatically and it's carefully designed
|
|
to bridge the semantic differences between Lua and C.
|
|
to bridge the semantic differences between Lua and C.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">⑥</span> Now that we have defined
|
|
|
|
|
|
+<span class="mark">⑥</span> Now that we have defined
|
|
our own <tt>sleep()</tt> function, we can just call it from plain Lua
|
|
our own <tt>sleep()</tt> function, we can just call it from plain Lua
|
|
code. That wasn't so bad, huh? Turning these boring animated dots into
|
|
code. That wasn't so bad, huh? Turning these boring animated dots into
|
|
a fascinating best-selling game is left as an exercise for the reader.
|
|
a fascinating best-selling game is left as an exercise for the reader.
|
|
@@ -196,27 +220,54 @@ href="http://zlib.net/">zlib</a> compression library from Lua code.
|
|
We'll define two convenience wrapper functions that take a string and
|
|
We'll define two convenience wrapper functions that take a string and
|
|
compress or uncompress it to another string:
|
|
compress or uncompress it to another string:
|
|
</p>
|
|
</p>
|
|
-<pre class="code">
|
|
|
|
-local ffi = require("ffi")
|
|
|
|
-ffi.cdef[[ <span style="color:#f0f4ff;">//</span><span style="color:#4040c0;">①</span>
|
|
|
|
|
|
+<pre class="code mark">
|
|
|
|
+<span class="codemark">
|
|
|
|
+①
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+②
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+③
|
|
|
|
+
|
|
|
|
+④
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+⑤
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+⑥
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+⑦</span>local ffi = require("ffi")
|
|
|
|
+ffi.cdef[[
|
|
<span style="color:#00a000;">unsigned long compressBound(unsigned long sourceLen);
|
|
<span style="color:#00a000;">unsigned long compressBound(unsigned long sourceLen);
|
|
int compress2(uint8_t *dest, unsigned long *destLen,
|
|
int compress2(uint8_t *dest, unsigned long *destLen,
|
|
const uint8_t *source, unsigned long sourceLen, int level);
|
|
const uint8_t *source, unsigned long sourceLen, int level);
|
|
int uncompress(uint8_t *dest, unsigned long *destLen,
|
|
int uncompress(uint8_t *dest, unsigned long *destLen,
|
|
const uint8_t *source, unsigned long sourceLen);</span>
|
|
const uint8_t *source, unsigned long sourceLen);</span>
|
|
]]
|
|
]]
|
|
-local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z") <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">②</span>
|
|
|
|
|
|
+local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
|
|
|
|
|
|
local function compress(txt)
|
|
local function compress(txt)
|
|
- local n = zlib.compressBound(#txt) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">③</span>
|
|
|
|
|
|
+ local n = zlib.compressBound(#txt)
|
|
local buf = ffi.new("uint8_t[?]", n)
|
|
local buf = ffi.new("uint8_t[?]", n)
|
|
- local buflen = ffi.new("unsigned long[1]", n) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">④</span>
|
|
|
|
|
|
+ local buflen = ffi.new("unsigned long[1]", n)
|
|
local res = zlib.compress2(buf, buflen, txt, #txt, 9)
|
|
local res = zlib.compress2(buf, buflen, txt, #txt, 9)
|
|
assert(res == 0)
|
|
assert(res == 0)
|
|
- return ffi.string(buf, buflen[0]) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">⑤</span>
|
|
|
|
|
|
+ return ffi.string(buf, buflen[0])
|
|
end
|
|
end
|
|
|
|
|
|
-local function uncompress(comp, n) <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">⑥</span>
|
|
|
|
|
|
+local function uncompress(comp, n)
|
|
local buf = ffi.new("uint8_t[?]", n)
|
|
local buf = ffi.new("uint8_t[?]", n)
|
|
local buflen = ffi.new("unsigned long[1]", n)
|
|
local buflen = ffi.new("unsigned long[1]", n)
|
|
local res = zlib.uncompress(buf, buflen, comp, #comp)
|
|
local res = zlib.uncompress(buf, buflen, comp, #comp)
|
|
@@ -224,7 +275,7 @@ local function uncompress(comp, n) <span style="color:#f0f4ff;">--</span><span s
|
|
return ffi.string(buf, buflen[0])
|
|
return ffi.string(buf, buflen[0])
|
|
end
|
|
end
|
|
|
|
|
|
--- Simple test code. <span style="color:#f0f4ff;">--</span><span style="color:#4040c0;">⑦</span>
|
|
|
|
|
|
+-- Simple test code.
|
|
local txt = string.rep("abcd", 1000)
|
|
local txt = string.rep("abcd", 1000)
|
|
print("Uncompressed size: ", #txt)
|
|
print("Uncompressed size: ", #txt)
|
|
local c = compress(txt)
|
|
local c = compress(txt)
|
|
@@ -236,13 +287,13 @@ assert(txt2 == txt)
|
|
Here's the step-by-step explanation:
|
|
Here's the step-by-step explanation:
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">①</span> This defines some of the
|
|
|
|
|
|
+<span class="mark">①</span> This defines some of the
|
|
C functions provided by zlib. For the sake of this example, some
|
|
C functions provided by zlib. For the sake of this example, some
|
|
type indirections have been reduced and it uses the pre-defined
|
|
type indirections have been reduced and it uses the pre-defined
|
|
fixed-size integer types, while still adhering to the zlib API/ABI.
|
|
fixed-size integer types, while still adhering to the zlib API/ABI.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">②</span> This loads the zlib shared
|
|
|
|
|
|
+<span class="mark">②</span> This loads the zlib shared
|
|
library. On POSIX systems it's named <tt>libz.so</tt> and usually
|
|
library. On POSIX systems it's named <tt>libz.so</tt> and usually
|
|
comes pre-installed. Since <tt>ffi.load()</tt> automatically adds any
|
|
comes pre-installed. Since <tt>ffi.load()</tt> automatically adds any
|
|
missing standard prefixes/suffixes, we can simply load the
|
|
missing standard prefixes/suffixes, we can simply load the
|
|
@@ -253,7 +304,7 @@ you'll have to download it first from the
|
|
<tt>ffi.load()</tt>.
|
|
<tt>ffi.load()</tt>.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">③</span> First, the maximum size of
|
|
|
|
|
|
+<span class="mark">③</span> First, the maximum size of
|
|
the compression buffer is obtained by calling the
|
|
the compression buffer is obtained by calling the
|
|
<tt>zlib.compressBound</tt> function with the length of the
|
|
<tt>zlib.compressBound</tt> function with the length of the
|
|
uncompressed string. The next line allocates a byte buffer of this
|
|
uncompressed string. The next line allocates a byte buffer of this
|
|
@@ -262,7 +313,7 @@ variable-length array (VLA). The actual number of elements of this
|
|
array is given as the 2nd argument to <tt>ffi.new()</tt>.
|
|
array is given as the 2nd argument to <tt>ffi.new()</tt>.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">④</span> This may look strange at
|
|
|
|
|
|
+<span class="mark">④</span> This may look strange at
|
|
first, but have a look at the declaration of the <tt>compress2</tt>
|
|
first, but have a look at the declaration of the <tt>compress2</tt>
|
|
function from zlib: the destination length is defined as a pointer!
|
|
function from zlib: the destination length is defined as a pointer!
|
|
This is because you pass in the maximum buffer size and get back the
|
|
This is because you pass in the maximum buffer size and get back the
|
|
@@ -276,7 +327,7 @@ initialized with the maximum buffer size in one step. Calling the
|
|
actual <tt>zlib.compress2</tt> function is then straightforward.
|
|
actual <tt>zlib.compress2</tt> function is then straightforward.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">⑤</span> We want to return the
|
|
|
|
|
|
+<span class="mark">⑤</span> We want to return the
|
|
compressed data as a Lua string, so we'll use <tt>ffi.string()</tt>.
|
|
compressed data as a Lua string, so we'll use <tt>ffi.string()</tt>.
|
|
It needs a pointer to the start of the data and the actual length. The
|
|
It needs a pointer to the start of the data and the actual length. The
|
|
length has been returned in the <tt>buflen</tt> array, so we'll just
|
|
length has been returned in the <tt>buflen</tt> array, so we'll just
|
|
@@ -292,13 +343,13 @@ results in buffers instead of strings. This will reduce the overhead
|
|
for garbage collection and string interning.
|
|
for garbage collection and string interning.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">⑥</span> The <tt>uncompress</tt>
|
|
|
|
|
|
+<span class="mark">⑥</span> The <tt>uncompress</tt>
|
|
functions does the exact opposite of the <tt>compress</tt> function.
|
|
functions does the exact opposite of the <tt>compress</tt> function.
|
|
The compressed data doesn't include the size of the original string,
|
|
The compressed data doesn't include the size of the original string,
|
|
so this needs to be passed in. Otherwise no surprises here.
|
|
so this needs to be passed in. Otherwise no surprises here.
|
|
</p>
|
|
</p>
|
|
<p>
|
|
<p>
|
|
-<span style="color:#4040c0;">⑦</span> The code, that makes use
|
|
|
|
|
|
+<span class="mark">⑦</span> The code, that makes use
|
|
of the functions we just defined, is just plain Lua code. It doesn't
|
|
of the functions we just defined, is just plain Lua code. It doesn't
|
|
need to know anything about the LuaJIT FFI — the convenience
|
|
need to know anything about the LuaJIT FFI — the convenience
|
|
wrapper functions completely hide it.
|
|
wrapper functions completely hide it.
|