mirror of
https://github.com/LuaJIT/LuaJIT.git
synced 2025-02-07 23:24:09 +00:00
String buffers, part 2d: basic string buffer methods.
Sponsored by fmad.io.
This commit is contained in:
parent
edd5cbadc5
commit
a119497bec
@ -1,19 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>String Buffers</title>
|
<title>String Buffer Library</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="Copyright" content="Copyright (C) 2005-2021">
|
<meta name="Copyright" content="Copyright (C) 2005-2021">
|
||||||
<meta name="Language" content="en">
|
<meta name="Language" content="en">
|
||||||
<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">
|
||||||
|
.lib {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 60%;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #c5d5ff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site">
|
<div id="site">
|
||||||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||||
</div>
|
</div>
|
||||||
<div id="head">
|
<div id="head">
|
||||||
<h1>String Buffers</h1>
|
<h1>String Buffer Library</h1>
|
||||||
</div>
|
</div>
|
||||||
<div id="nav">
|
<div id="nav">
|
||||||
<ul><li>
|
<ul><li>
|
||||||
@ -57,31 +68,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The string buffer library allows <b>high-performance manipulation of
|
The string buffer library allows <b>high-performance manipulation of
|
||||||
string-like data</b>.
|
string-like data</b>.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
Unlike Lua strings, which are constants, string buffers are
|
Unlike Lua strings, which are constants, string buffers are
|
||||||
<b>mutable</b> sequences of 8-bit (binary-transparent) characters. Data
|
<b>mutable</b> sequences of 8-bit (binary-transparent) characters. Data
|
||||||
can be stored, formatted and encoded into a string buffer and later
|
can be stored, formatted and encoded into a string buffer and later
|
||||||
converted, decoded or extracted.
|
converted, extracted or decoded.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The convenient string buffer API simplifies common string manipulation
|
The convenient string buffer API simplifies common string manipulation
|
||||||
tasks, that would otherwise require creating many intermediate strings.
|
tasks, that would otherwise require creating many intermediate strings.
|
||||||
String buffers improve performance by eliminating redundant memory
|
String buffers improve performance by eliminating redundant memory
|
||||||
copies, object creation, string interning and garbage collection
|
copies, object creation, string interning and garbage collection
|
||||||
overhead. In conjunction with the FFI library, they allow zero-copy
|
overhead. In conjunction with the FFI library, they allow zero-copy
|
||||||
operations.
|
operations.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The string buffer libary also includes a high-performance
|
||||||
|
<a href="serialize">serializer</a> for Lua objects.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="load">Using the String Buffer Library</h2>
|
<h2 id="wip" style="color:#ff0000">Work in Progress</h2>
|
||||||
|
<p>
|
||||||
|
<b style="color:#ff0000">This library is a work in progress. More
|
||||||
|
functionality will be added soon.</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="use">Using the String Buffer Library</h2>
|
||||||
<p>
|
<p>
|
||||||
The string buffer library is built into LuaJIT by default, but it's not
|
The string buffer library is built into LuaJIT by default, but it's not
|
||||||
loaded by default. Add this to the start of every Lua file that needs
|
loaded by default. Add this to the start of every Lua file that needs
|
||||||
@ -90,137 +105,406 @@ one of its functions:
|
|||||||
<pre class="code">
|
<pre class="code">
|
||||||
local buffer = require("string.buffer")
|
local buffer = require("string.buffer")
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h2 id="wip" style="color:#ff0000">Work in Progress</h2>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
The convention for the syntax shown on this page is that <tt>buffer</tt>
|
||||||
|
refers to the buffer library and <tt>buf</tt> refers to an individual
|
||||||
|
buffer object.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please note the difference between a Lua function call, e.g.
|
||||||
|
<tt>buffer.new()</tt> (with a dot) and a Lua method call, e.g.
|
||||||
|
<tt>buf:reset()</tt> (with a colon).
|
||||||
|
</p>
|
||||||
|
|
||||||
<b style="color:#ff0000">This library is a work in progress. More
|
<h3 id="buffer_object">Buffer Objects</h3>
|
||||||
functions will be added soon.</b>
|
<p>
|
||||||
|
A buffer object is a garbage-collected Lua object. After creation with
|
||||||
|
<tt>buffer.new()</tt>, it can (and should) be reused for many operations.
|
||||||
|
When the last reference to a buffer object is gone, it will eventually
|
||||||
|
be freed by the garbage collector, along with the allocated buffer
|
||||||
|
space.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Buffers operate like a FIFO (first-in first-out) data structure. Data
|
||||||
|
can be appended (written) to the end of the buffer and consumed (read)
|
||||||
|
from the front of the buffer. These operations can be freely mixed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The buffer space that holds the characters is managed automatically
|
||||||
|
— it grows as needed and already consumed space is recycled. Use
|
||||||
|
<tt>buffer.new(size)</tt> and <tt>buf:free()</tt>, if you need more
|
||||||
|
control.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The maximum size of a single buffer is the same as the maximum size of a
|
||||||
|
Lua string, which is slightly below two gigabytes. For huge data sizes,
|
||||||
|
neither strings nor buffers are the right data structure — use the
|
||||||
|
FFI library to directly map memory or files up to the virtual memory
|
||||||
|
limit of your OS.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_overview">Buffer Method Overview</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
The <tt>buf:put*()</tt>-like methods append (write) characters to the
|
||||||
|
end of the buffer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The <tt>buf:get*()</tt>-like methods consume (read) characters from the
|
||||||
|
front of the buffer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Other methods, like <tt>buf:tostring()</tt> only read the buffer
|
||||||
|
contents, but don't change the buffer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The <tt>buf:set()</tt> method allows zero-copy consumption of a string
|
||||||
|
or an FFI cdata object as a buffer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The FFI-specific methods allow zero-copy read/write-style operations or
|
||||||
|
modifying the buffer contents in-place. Please check the
|
||||||
|
<a href="#ffi_caveats">FFI caveats</a> below, too.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Methods that don't need to return anything specific, return the buffer
|
||||||
|
object itself as a convenience. This allows method chaining, e.g.:
|
||||||
|
<tt>buf:reset():encode(obj)</tt> or <tt>buf:skip(len):get()</tt>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="create">Buffer Creation and Management</h2>
|
||||||
|
|
||||||
|
<h3 id="buffer_new"><tt>local buf = buffer.new([size])</tt></h3>
|
||||||
|
<p>
|
||||||
|
Creates a new buffer object.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The optional <tt>size</tt> argument ensures a minimum initial buffer
|
||||||
|
size. This is strictly an optimization for cases where the required
|
||||||
|
buffer size is known beforehand.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_reset"><tt>buf = buf:reset()</tt></h3>
|
||||||
|
<p>
|
||||||
|
Reset (empty) the buffer. The allocated buffer space is not freed and
|
||||||
|
may be reused.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_free"><tt>buf = buf:free()</tt></h3>
|
||||||
|
<p>
|
||||||
|
The buffer space of the buffer object is freed. The object itself
|
||||||
|
remains intact, empty and it may be reused.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Note: you normally don't need to use this method. The garbage collector
|
||||||
|
automatically frees the buffer space, when the buffer object is
|
||||||
|
collected. Use this method, if you need to free the associated memory
|
||||||
|
immediately.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="write">Buffer Writers</h2>
|
||||||
|
|
||||||
|
<h3 id="buffer_put"><tt>buf = buf:put([str|num|obj] [, ...])</tt></h3>
|
||||||
|
<p>
|
||||||
|
Appends a string <tt>str</tt>, a number <tt>num</tt> or any object
|
||||||
|
<tt>obj</tt> with a <tt>__tostring</tt> metamethod to the buffer.
|
||||||
|
Multiple arguments are appended in the given order.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Appending a buffer to a buffer is possible and short-circuited
|
||||||
|
internally. But it still involves a copy. Better combine the buffer
|
||||||
|
writes to use a single buffer.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_putf"><tt>buf = buf:putf(format, ...)</tt></h3>
|
||||||
|
<p>
|
||||||
|
Appends the formatted arguments to the buffer. The <tt>format</tt>
|
||||||
|
string supports the same options as <tt>string.format()</tt>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_putcdata"><tt>buf = buf:putcdata(cdata, len)</tt><span class="lib">FFI</span></h3>
|
||||||
|
<p>
|
||||||
|
Appends the given <tt>len</tt> number of bytes from the memory pointed
|
||||||
|
to by the FFI <tt>cdata</tt> object to the buffer. The object needs to
|
||||||
|
be convertible to a (constant) pointer.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_set"><tt>buf = buf:set(str)<br>
|
||||||
|
buf = buf:set(cdata, len)</tt><span class="lib">FFI</span></h3>
|
||||||
|
<p>
|
||||||
|
This method allows zero-copy consumption of a string or an FFI cdata
|
||||||
|
object as a buffer. It stores a reference to the passed string
|
||||||
|
<tt>str</tt> or the FFI <tt>cdata</tt> object in the buffer. Any buffer
|
||||||
|
space originally allocated is freed. This is <i>not</i> an append
|
||||||
|
operation, unlike the <tt>buf:put*()</tt> methods.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
After calling this method, the buffer behaves as if
|
||||||
|
<tt>buf:free():put(str)</tt> or <tt>buf:free():put(cdata, len)</tt>
|
||||||
|
had been called. However, the data is only referenced and not copied, as
|
||||||
|
long as the buffer is only consumed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In case the buffer is written to later on, the referenced data is copied
|
||||||
|
and the object reference is removed (copy-on-write semantics).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The stored reference is an anchor for the garbage collector and keeps the
|
||||||
|
originally passed string or FFI cdata object alive.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_reserve"><tt>ptr, len = buf:reserve(size)</tt><span class="lib">FFI</span><br>
|
||||||
|
<tt>buf = buf:commit(used)</tt><span class="lib">FFI</span></h3>
|
||||||
|
<p>
|
||||||
|
The <tt>reserve</tt> method reserves at least <tt>size</tt> bytes of
|
||||||
|
write space in the buffer. It returns an <tt>uint8_t *</tt> FFI
|
||||||
|
cdata pointer <tt>ptr</tt> that points to this space.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The available length in bytes is returned in <tt>len</tt>. This is at
|
||||||
|
least <tt>size</tt> bytes, but may be more to facilitate efficient
|
||||||
|
buffer growth. You can either make use of the additional space or ignore
|
||||||
|
<tt>len</tt> and only use <tt>size</tt> bytes.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The <tt>commit</tt> method appends the <tt>used</tt> bytes of the
|
||||||
|
previously returned write space to the buffer data.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This pair of methods allows zero-copy use of C read-style APIs:
|
||||||
|
</p>
|
||||||
|
<pre class="code">
|
||||||
|
local MIN_SIZE = 65536
|
||||||
|
repeat
|
||||||
|
local ptr, len = buf:reserve(MIN_SIZE)
|
||||||
|
local n = C.read(fd, ptr, len)
|
||||||
|
if n == 0 then break end -- EOF.
|
||||||
|
if n < 0 then error("read error") end
|
||||||
|
buf:commit(n)
|
||||||
|
until false
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
The reserved write space is <i>not</i> initialized. At least the
|
||||||
|
<tt>used</tt> bytes <b>must</b> be written to before calling the
|
||||||
|
<tt>commit</tt> method. There's no need to call the <tt>commit</tt>
|
||||||
|
method, if nothing is added to the buffer (e.g. on error).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="read">Buffer Readers</h2>
|
||||||
|
|
||||||
|
<h3 id="buffer_length"><tt>len = #buf</tt></h3>
|
||||||
|
<p>
|
||||||
|
Returns the current length of the buffer data in bytes.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_concat"><tt>res = str|num|buf .. str|num|buf [...]</tt></h3>
|
||||||
|
<p>
|
||||||
|
The Lua concatenation operator <tt>..</tt> also accepts buffers, just
|
||||||
|
like strings or numbers. It always returns a string and not a buffer.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Note that although this is supported for convenience, this thwarts one
|
||||||
|
of the main reasons to use buffers, which is to avoid string
|
||||||
|
allocations. Rewrite it with <tt>buf:put()</tt> and <tt>buf:get()</tt>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Mixing this with unrelated objects that have a <tt>__concat</tt>
|
||||||
|
metamethod may not work, since these probably only expect strings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_skip"><tt>buf = buf:skip(len)</tt></h3>
|
||||||
|
<p>
|
||||||
|
Skips (consumes) <tt>len</tt> bytes from the buffer up to the current
|
||||||
|
length of the buffer data.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_get"><tt>str, ... = buf:get([len|nil] [,...])</tt></h3>
|
||||||
|
<p>
|
||||||
|
Consumes the buffer data and returns one or more strings. If called
|
||||||
|
without arguments, the whole buffer data is consumed. If called with a
|
||||||
|
number, up to <tt>len</tt> bytes are consumed. A <tt>nil</tt> argument
|
||||||
|
consumes the remaining buffer space (this only makes sense as the last
|
||||||
|
argument). Multiple arguments consume the buffer data in the given
|
||||||
|
order.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Note: a zero length or no remaining buffer data returns an empty string
|
||||||
|
and not <tt>nil</tt>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_tostring"><tt>str = buf:tostring()<br>
|
||||||
|
str = tostring(buf)</tt></h3>
|
||||||
|
<p>
|
||||||
|
Creates a string from the buffer data, but doesn't consume it. The
|
||||||
|
buffer remains unchanged.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Buffer objects also define a <tt>__tostring</tt> metamethod. This means
|
||||||
|
buffers can be passed to the global <tt>tostring()</tt> function and
|
||||||
|
many other functions that accept this in place of strings. The important
|
||||||
|
internal uses in functions like <tt>io.write()</tt> are short-circuited
|
||||||
|
to avoid the creation of an intermediate string object.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="buffer_ref"><tt>ptr, len = buf:ref()</tt><span class="lib">FFI</span></h3>
|
||||||
|
<p>
|
||||||
|
Returns an <tt>uint8_t *</tt> FFI cdata pointer <tt>ptr</tt> that
|
||||||
|
points to the buffer data. The length of the buffer data in bytes is
|
||||||
|
returned in <tt>len</tt>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The returned pointer can be directly passed to C functions that expect a
|
||||||
|
buffer and a length. You can also do bytewise reads
|
||||||
|
(<tt>local x = ptr[i]</tt>) or writes
|
||||||
|
(<tt>ptr[i] = 0x40</tt>) of the buffer data.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In conjunction with the <tt>skip</tt> method, this allows zero-copy use
|
||||||
|
of C write-style APIs:
|
||||||
|
</p>
|
||||||
|
<pre class="code">
|
||||||
|
repeat
|
||||||
|
local ptr, len = buf:ref()
|
||||||
|
if len == 0 then break end
|
||||||
|
local n = C.write(fd, ptr, len)
|
||||||
|
if n < 0 then error("write error") end
|
||||||
|
buf:skip(n)
|
||||||
|
until n >= len
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Unlike Lua strings, buffer data is <i>not</i> implicitly
|
||||||
|
zero-terminated. It's not safe to pass <tt>ptr</tt> to C functions that
|
||||||
|
expect zero-terminated strings. If you're not using <tt>len</tt>, then
|
||||||
|
you're doing something wrong.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="serialize">Serialization of Lua Objects</h2>
|
<h2 id="serialize">Serialization of Lua Objects</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The following functions and methods allow <b>high-speed serialization</b>
|
The following functions and methods allow <b>high-speed serialization</b>
|
||||||
(encoding) of a Lua object into a string and decoding it back to a Lua
|
(encoding) of a Lua object into a string and decoding it back to a Lua
|
||||||
object. This allows convenient storage and transport of <b>structured
|
object. This allows convenient storage and transport of <b>structured
|
||||||
data</b>.
|
data</b>.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The encoded data is in an <a href="#serialize_format">internal binary
|
The encoded data is in an <a href="#serialize_format">internal binary
|
||||||
format</a>. The data can be stored in files, binary-transparent
|
format</a>. The data can be stored in files, binary-transparent
|
||||||
databases or transmitted to other LuaJIT instances across threads,
|
databases or transmitted to other LuaJIT instances across threads,
|
||||||
processes or networks.
|
processes or networks.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
Encoding speed can reach up to 1 Gigabyte/second on a modern desktop- or
|
Encoding speed can reach up to 1 Gigabyte/second on a modern desktop- or
|
||||||
server-class system, even when serializing many small objects. Decoding
|
server-class system, even when serializing many small objects. Decoding
|
||||||
speed is mostly constrained by object creation cost.
|
speed is mostly constrained by object creation cost.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The serializer handles most Lua types, common FFI number types and
|
The serializer handles most Lua types, common FFI number types and
|
||||||
nested structures. Functions, thread objects, other FFI cdata, full
|
nested structures. Functions, thread objects, other FFI cdata, full
|
||||||
userdata and associated metatables cannot be serialized (yet).
|
userdata and associated metatables cannot be serialized (yet).
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The encoder serializes nested structures as trees. Multiple references
|
The encoder serializes nested structures as trees. Multiple references
|
||||||
to a single object will be stored separately and create distinct objects
|
to a single object will be stored separately and create distinct objects
|
||||||
after decoding. Circular references cause an error.
|
after decoding. Circular references cause an error.
|
||||||
|
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 id="buffer_encode"><tt>str = buffer.encode(obj)</tt></h3>
|
<h3 id="serialize_methods">Serialization Functions and Methods</h3>
|
||||||
|
|
||||||
|
<h3 id="buffer_encode"><tt>str = buffer.encode(obj)<br>
|
||||||
|
buf = buf:encode(obj)</tt></h3>
|
||||||
<p>
|
<p>
|
||||||
|
Serializes (encodes) the Lua object <tt>obj</tt>. The stand-alone
|
||||||
Serializes (encodes) the Lua object <tt>obj</tt> into the string
|
function returns a string <tt>str</tt>. The buffer method appends the
|
||||||
<tt>str</tt>.
|
encoding to the buffer.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
<tt>obj</tt> can be any of the supported Lua types — it doesn't
|
<tt>obj</tt> can be any of the supported Lua types — it doesn't
|
||||||
need to be a Lua table.
|
need to be a Lua table.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
This function may throw an error when attempting to serialize
|
This function may throw an error when attempting to serialize
|
||||||
unsupported object types, circular references or deeply nested tables.
|
unsupported object types, circular references or deeply nested tables.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 id="buffer_decode"><tt>obj = buffer.decode(str)</tt></h3>
|
<h3 id="buffer_decode"><tt>obj = buffer.decode(str)<br>
|
||||||
|
obj = buf:decode()</tt></h3>
|
||||||
<p>
|
<p>
|
||||||
|
The stand-alone function de-serializes (decodes) the string
|
||||||
De-serializes (decodes) the string <tt>str</tt> into the Lua object
|
<tt>str</tt>, the buffer method de-serializes one object from the
|
||||||
<tt>obj</tt>.
|
buffer. Both return a Lua object <tt>obj</tt>.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The returned object may be any of the supported Lua types —
|
The returned object may be any of the supported Lua types —
|
||||||
even <tt>nil</tt>.
|
even <tt>nil</tt>.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
This function may throw an error when fed with malformed or incomplete
|
This function may throw an error when fed with malformed or incomplete
|
||||||
encoded data. The standalone function throws when there's left-over data
|
encoded data. The stand-alone function throws when there's left-over
|
||||||
after decoding a single top-level object.
|
data after decoding a single top-level object. The buffer method leaves
|
||||||
|
any left-over data in the buffer.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="serialize_format">Serialization Format Specification</h2>
|
<h3 id="serialize_stream">Streaming Serialization</h3>
|
||||||
<p>
|
<p>
|
||||||
|
In some contexts, it's desirable to do piecewise serialization of large
|
||||||
|
datasets, also known as <i>streaming</i>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This serialization format can be safely concatenated and supports streaming.
|
||||||
|
Multiple encodings can simply be appended to a buffer and later decoded
|
||||||
|
individually:
|
||||||
|
</p>
|
||||||
|
<pre class="code">
|
||||||
|
local buf = buffer.new()
|
||||||
|
buf:encode(obj1)
|
||||||
|
buf:encode(obj2)
|
||||||
|
local copy1 = buf:decode()
|
||||||
|
local copy2 = buf:decode()
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Here's how to iterate over a stream:
|
||||||
|
</p>
|
||||||
|
<pre class="code">
|
||||||
|
while #buf ~= 0 do
|
||||||
|
local obj = buf:decode()
|
||||||
|
-- Do something with obj.
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Since the serialization format doesn't prepend a length to its encoding,
|
||||||
|
network applications may need to transmit the length, too.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="serialize_format">Serialization Format Specification</h3>
|
||||||
|
<p>
|
||||||
This serialization format is designed for <b>internal use</b> by LuaJIT
|
This serialization format is designed for <b>internal use</b> by LuaJIT
|
||||||
applications. Serialized data is upwards-compatible and portable across
|
applications. Serialized data is upwards-compatible and portable across
|
||||||
all supported LuaJIT platforms.
|
all supported LuaJIT platforms.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
It's an <b>8-bit binary format</b> and not human-readable. It uses e.g.
|
It's an <b>8-bit binary format</b> and not human-readable. It uses e.g.
|
||||||
embedded zeroes and stores embedded Lua string objects unmodified, which
|
embedded zeroes and stores embedded Lua string objects unmodified, which
|
||||||
are 8-bit-clean, too. Encoded data can be safely concatenated for
|
are 8-bit-clean, too. Encoded data can be safely concatenated for
|
||||||
streaming and later decoded one top-level object at a time.
|
streaming and later decoded one top-level object at a time.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The encoding is reasonably compact, but tuned for maximum performance,
|
The encoding is reasonably compact, but tuned for maximum performance,
|
||||||
not for minimum space usage. It compresses well with any of the common
|
not for minimum space usage. It compresses well with any of the common
|
||||||
byte-oriented data compression algorithms.
|
byte-oriented data compression algorithms.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
Although documented here for reference, this format is explicitly
|
Although documented here for reference, this format is explicitly
|
||||||
<b>not</b> intended to be a 'public standard' for structured data
|
<b>not</b> intended to be a 'public standard' for structured data
|
||||||
interchange across computer languages (like JSON or MessagePack). Please
|
interchange across computer languages (like JSON or MessagePack). Please
|
||||||
do not use it as such.
|
do not use it as such.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
The specification is given below as a context-free grammar with a
|
The specification is given below as a context-free grammar with a
|
||||||
top-level <tt>object</tt> as the starting point. Alternatives are
|
top-level <tt>object</tt> as the starting point. Alternatives are
|
||||||
separated by the <tt>|</tt> symbol and <tt>*</tt> indicates repeats.
|
separated by the <tt>|</tt> symbol and <tt>*</tt> indicates repeats.
|
||||||
Grouping is implicit or indicated by <tt>{…}</tt>. Terminals are
|
Grouping is implicit or indicated by <tt>{…}</tt>. Terminals are
|
||||||
either plain hex numbers, encoded as bytes, or have a <tt>.format</tt>
|
either plain hex numbers, encoded as bytes, or have a <tt>.format</tt>
|
||||||
suffix.
|
suffix.
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
object → nil | false | true
|
object → nil | false | true
|
||||||
@ -261,6 +545,73 @@ string → (0x20+len).U len*char.B
|
|||||||
0xe0..0x1fdf → (0xe0|(((n-0xe0)>>8)&0x1f)).B ((n-0xe0)&0xff).B
|
0xe0..0x1fdf → (0xe0|(((n-0xe0)>>8)&0x1f)).B ((n-0xe0)&0xff).B
|
||||||
0x1fe0.. → 0xff n.I
|
0x1fe0.. → 0xff n.I
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="error">Error handling</h2>
|
||||||
|
<p>
|
||||||
|
Many of the buffer methods can throw an error. Out-of-memory or usage
|
||||||
|
errors are best caught with an outer wrapper for larger parts of code.
|
||||||
|
There's not much one can do after that, anyway.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
OTOH you may want to catch some errors individually. Buffer methods need
|
||||||
|
to receive the buffer object as the first argument. The Lua colon-syntax
|
||||||
|
<tt>obj:method()</tt> does that implicitly. But to wrap a method with
|
||||||
|
<tt>pcall()</tt>, the arguments need to be passed like this:
|
||||||
|
</p>
|
||||||
|
<pre class="code">
|
||||||
|
local ok, err = pcall(buf.encode, buf, obj)
|
||||||
|
if not ok then
|
||||||
|
-- Handle error in err.
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="ffi_caveats">FFI caveats</h2>
|
||||||
|
<p>
|
||||||
|
The string buffer library has been designed to work well together with
|
||||||
|
the FFI library. But due to the low-level nature of the FFI library,
|
||||||
|
some care needs to be taken:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
First, please remember that FFI pointers are zero-indexed. The space
|
||||||
|
returned by <tt>buf:reserve()</tt> and <tt>buf:ref()</tt> starts at the
|
||||||
|
returned pointer and ends before <tt>len</tt> bytes after that.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I.e. the first valid index is <tt>ptr[0]</tt> and the last valid index
|
||||||
|
is <tt>ptr[len-1]</tt>. If the returned length is zero, there's no valid
|
||||||
|
index at all. The returned pointer may even be <tt>NULL</tt>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The space pointed to by the returned pointer is only valid as long as
|
||||||
|
the buffer is not modified in any way (neither append, nor consume, nor
|
||||||
|
reset, etc.). The pointer is also not a GC anchor for the buffer object
|
||||||
|
itself.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Buffer data is only guaranteed to be byte-aligned. Casting the returned
|
||||||
|
pointer to a data type with higher alignment may cause unaligned
|
||||||
|
accesses. It depends on the CPU architecture whether this is allowed or
|
||||||
|
not (it's always OK on x86/x64 and mostly OK on other modern
|
||||||
|
architectures).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
FFI pointers or references do not count as GC anchors for an underlying
|
||||||
|
object. E.g. an <tt>array</tt> allocated with <tt>ffi.new()</tt> is
|
||||||
|
anchored by <tt>buf:set(array, len)</tt>, but not by
|
||||||
|
<tt>buf:set(array+offset, len)</tt>. The addition of the offset
|
||||||
|
creates a new pointer, even when the offset is zero. In this case, you
|
||||||
|
need to make sure there's still a reference to the original array as
|
||||||
|
long as its contents are in use by the buffer.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Even though each LuaJIT VM instance is single-threaded (but you can
|
||||||
|
create multiple VMs), FFI data structures can be accessed concurrently.
|
||||||
|
Be careful when reading/writing FFI cdata from/to buffers to avoid
|
||||||
|
concurrent accesses or modifications. In particular, the memory
|
||||||
|
referenced by <tt>buf:set(cdata, len)</tt> must not be modified
|
||||||
|
while buffer readers are working on it. Shared, but read-only memory
|
||||||
|
mappings of files are OK, but only if the file does not change.
|
||||||
|
</p>
|
||||||
<br class="flush">
|
<br class="flush">
|
||||||
</div>
|
</div>
|
||||||
<div id="foot">
|
<div id="foot">
|
||||||
|
@ -2,17 +2,18 @@ lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
|||||||
lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \
|
lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \
|
||||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h
|
lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h
|
||||||
lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h \
|
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h \
|
||||||
lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cconv.h \
|
lj_str.h lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \
|
||||||
lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h lj_strscan.h \
|
lj_cconv.h lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h \
|
||||||
lj_strfmt.h lj_lib.h lj_libdef.h
|
lj_strscan.h lj_strfmt.h lj_lib.h lj_libdef.h
|
||||||
lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||||
lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_strscan.h \
|
lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_strscan.h \
|
||||||
lj_strfmt.h lj_ctype.h lj_cdata.h lj_cconv.h lj_carith.h lj_ff.h \
|
lj_strfmt.h lj_ctype.h lj_cdata.h lj_cconv.h lj_carith.h lj_ff.h \
|
||||||
lj_ffdef.h lj_lib.h lj_libdef.h
|
lj_ffdef.h lj_lib.h lj_libdef.h
|
||||||
lib_buffer.o: lib_buffer.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
lib_buffer.o: lib_buffer.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||||
lj_def.h lj_arch.h lj_gc.h lj_buf.h lj_str.h lj_serialize.h lj_lib.h \
|
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
|
||||||
lj_libdef.h
|
lj_tab.h lj_udata.h lj_meta.h lj_ctype.h lj_cdata.h lj_cconv.h \
|
||||||
|
lj_strfmt.h lj_serialize.h lj_lib.h lj_libdef.h
|
||||||
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \
|
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \
|
||||||
lj_libdef.h
|
lj_libdef.h
|
||||||
@ -51,10 +52,10 @@ lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
|||||||
lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
|
lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
|
||||||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h lj_strfmt.h
|
lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h lj_strfmt.h
|
||||||
lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||||
lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \
|
lj_buf.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h \
|
||||||
lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
lj_jit.h lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h \
|
||||||
lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_emit_*.h \
|
lj_traceerr.h lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h \
|
||||||
lj_asm_*.h
|
lj_emit_*.h lj_asm_*.h
|
||||||
lj_assert.o: lj_assert.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h
|
lj_assert.o: lj_assert.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h
|
||||||
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
|
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
|
||||||
lj_bcdef.h
|
lj_bcdef.h
|
||||||
@ -80,8 +81,8 @@ lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \
|
|||||||
lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \
|
lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \
|
||||||
lj_traceerr.h lj_vm.h
|
lj_traceerr.h lj_vm.h
|
||||||
lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||||
lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h \
|
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_ctype.h \
|
||||||
lj_ccallback.h
|
lj_cdata.h lj_cconv.h lj_ccallback.h
|
||||||
lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h
|
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h
|
||||||
lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h
|
lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h
|
||||||
@ -137,8 +138,8 @@ lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
|||||||
lj_strfmt.h
|
lj_strfmt.h
|
||||||
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
|
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
|
||||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \
|
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \
|
||||||
lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lex.h \
|
lj_dispatch.h lj_jit.h lj_ir.h lj_ctype.h lj_vm.h lj_strscan.h \
|
||||||
lj_bcdump.h lj_lib.h
|
lj_strfmt.h lj_lex.h lj_bcdump.h lj_lib.h
|
||||||
lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
||||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \
|
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \
|
||||||
lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
|
lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "lj_gc.h"
|
#include "lj_gc.h"
|
||||||
#include "lj_err.h"
|
#include "lj_err.h"
|
||||||
#include "lj_debug.h"
|
#include "lj_debug.h"
|
||||||
|
#include "lj_buf.h"
|
||||||
#include "lj_str.h"
|
#include "lj_str.h"
|
||||||
#include "lj_tab.h"
|
#include "lj_tab.h"
|
||||||
#include "lj_meta.h"
|
#include "lj_meta.h"
|
||||||
@ -406,10 +407,22 @@ LJLIB_CF(load)
|
|||||||
GCstr *name = lj_lib_optstr(L, 2);
|
GCstr *name = lj_lib_optstr(L, 2);
|
||||||
GCstr *mode = lj_lib_optstr(L, 3);
|
GCstr *mode = lj_lib_optstr(L, 3);
|
||||||
int status;
|
int status;
|
||||||
if (L->base < L->top && (tvisstr(L->base) || tvisnumber(L->base))) {
|
if (L->base < L->top &&
|
||||||
GCstr *s = lj_lib_checkstr(L, 1);
|
(tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) {
|
||||||
|
const char *s;
|
||||||
|
MSize len;
|
||||||
|
if (tvisbuf(L->base)) {
|
||||||
|
SBufExt *sbx = bufV(L->base);
|
||||||
|
s = sbx->r;
|
||||||
|
len = sbufxlen(sbx);
|
||||||
|
if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */
|
||||||
|
} else {
|
||||||
|
GCstr *str = lj_lib_checkstr(L, 1);
|
||||||
|
s = strdata(str);
|
||||||
|
len = str->len;
|
||||||
|
}
|
||||||
lua_settop(L, 4); /* Ensure env arg exists. */
|
lua_settop(L, 4); /* Ensure env arg exists. */
|
||||||
status = luaL_loadbufferx(L, strdata(s), s->len, strdata(name ? name : s),
|
status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s,
|
||||||
mode ? strdata(mode) : NULL);
|
mode ? strdata(mode) : NULL);
|
||||||
} else {
|
} else {
|
||||||
lj_lib_checkfunc(L, 1);
|
lj_lib_checkfunc(L, 1);
|
||||||
|
278
src/lib_buffer.c
278
src/lib_buffer.c
@ -14,14 +14,286 @@
|
|||||||
|
|
||||||
#if LJ_HASBUFFER
|
#if LJ_HASBUFFER
|
||||||
#include "lj_gc.h"
|
#include "lj_gc.h"
|
||||||
|
#include "lj_err.h"
|
||||||
#include "lj_buf.h"
|
#include "lj_buf.h"
|
||||||
|
#include "lj_str.h"
|
||||||
|
#include "lj_tab.h"
|
||||||
|
#include "lj_udata.h"
|
||||||
|
#include "lj_meta.h"
|
||||||
|
#if LJ_HASFFI
|
||||||
|
#include "lj_ctype.h"
|
||||||
|
#include "lj_cdata.h"
|
||||||
|
#include "lj_cconv.h"
|
||||||
|
#endif
|
||||||
|
#include "lj_strfmt.h"
|
||||||
#include "lj_serialize.h"
|
#include "lj_serialize.h"
|
||||||
#include "lj_lib.h"
|
#include "lj_lib.h"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define LJLIB_MODULE_buffer_method
|
||||||
|
|
||||||
|
/* Check that the first argument is a string buffer. */
|
||||||
|
static SBufExt *buffer_tobuf(lua_State *L)
|
||||||
|
{
|
||||||
|
if (!(L->base < L->top && tvisbuf(L->base)))
|
||||||
|
lj_err_argtype(L, 1, "buffer");
|
||||||
|
return bufV(L->base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ditto, but for writers. */
|
||||||
|
static LJ_AINLINE SBufExt *buffer_tobufw(lua_State *L)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
setsbufXL_(sbx, L);
|
||||||
|
return sbx;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_free)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
lj_bufx_free(G(L), sbx);
|
||||||
|
lj_bufx_init(L, sbx);
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_reset)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
lj_bufx_reset(sbx);
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_skip)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
MSize n = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||||
|
MSize len = sbufxlen(sbx);
|
||||||
|
if (n < len) {
|
||||||
|
sbx->r += n;
|
||||||
|
} else {
|
||||||
|
sbx->r = sbx->w = sbx->b;
|
||||||
|
}
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_set)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
const char *p;
|
||||||
|
MSize len;
|
||||||
|
#if LJ_HASFFI
|
||||||
|
if (tviscdata(L->base+1)) {
|
||||||
|
CTState *cts = ctype_cts(L);
|
||||||
|
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p,
|
||||||
|
L->base+1, CCF_ARG(2));
|
||||||
|
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
GCstr *str = lj_lib_checkstrx(L, 2);
|
||||||
|
p = strdata(str);
|
||||||
|
len = str->len;
|
||||||
|
}
|
||||||
|
lj_bufx_free(G(L), sbx);
|
||||||
|
lj_bufx_init_cow(L, sbx, p, len);
|
||||||
|
setgcref(sbx->cowref, gcV(L->base+1));
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_put)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
ptrdiff_t arg, narg = L->top - L->base;
|
||||||
|
for (arg = 1; arg < narg; arg++) {
|
||||||
|
cTValue *o = &L->base[arg], *mo = NULL;
|
||||||
|
retry:
|
||||||
|
if (tvisstr(o)) {
|
||||||
|
lj_buf_putstr((SBuf *)sbx, strV(o));
|
||||||
|
} else if (tvisint(o)) {
|
||||||
|
lj_strfmt_putint((SBuf *)sbx, intV(o));
|
||||||
|
} else if (tvisnum(o)) {
|
||||||
|
lj_strfmt_putfnum((SBuf *)sbx, STRFMT_G14, numV(o));
|
||||||
|
} else if (tvisbuf(o)) {
|
||||||
|
SBufExt *sbx2 = bufV(o);
|
||||||
|
lj_buf_putmem((SBuf *)sbx, sbx2->r, sbufxlen(sbx2));
|
||||||
|
} else if (!mo && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||||
|
/* Call __tostring metamethod inline. */
|
||||||
|
copyTV(L, L->top++, mo);
|
||||||
|
copyTV(L, L->top++, o);
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
o = &L->base[arg]; /* The stack may have been reallocated. */
|
||||||
|
copyTV(L, &L->base[arg], L->top-1);
|
||||||
|
L->top = L->base + narg;
|
||||||
|
goto retry; /* Retry with the result. */
|
||||||
|
} else {
|
||||||
|
lj_err_argtype(L, arg+1, "string/number/__tostring");
|
||||||
|
}
|
||||||
|
/* Probably not useful to inline other __tostring MMs, e.g. FFI numbers. */
|
||||||
|
}
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
lj_gc_check(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_putf)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
lj_strfmt_putarg(L, (SBuf *)sbx, 2, 2);
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
lj_gc_check(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_get)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
ptrdiff_t arg, narg = L->top - L->base;
|
||||||
|
if (narg == 1) {
|
||||||
|
narg++;
|
||||||
|
setnilV(L->top++); /* get() is the same as get(nil). */
|
||||||
|
}
|
||||||
|
for (arg = 1; arg < narg; arg++) {
|
||||||
|
TValue *o = &L->base[arg];
|
||||||
|
MSize n = tvisnil(o) ? LJ_MAX_BUF :
|
||||||
|
(MSize) lj_lib_checkintrange(L, arg+1, 0, LJ_MAX_BUF);
|
||||||
|
MSize len = sbufxlen(sbx);
|
||||||
|
if (n > len) n = len;
|
||||||
|
setstrV(L, o, lj_str_new(L, sbx->r, n));
|
||||||
|
sbx->r += n;
|
||||||
|
}
|
||||||
|
if (sbx->r == sbx->w) sbx->r = sbx->w = sbx->b;
|
||||||
|
lj_gc_check(L);
|
||||||
|
return narg-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LJ_HASFFI
|
||||||
|
LJLIB_CF(buffer_method_putcdata)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
const char *p;
|
||||||
|
MSize len;
|
||||||
|
if (tviscdata(L->base+1)) {
|
||||||
|
CTState *cts = ctype_cts(L);
|
||||||
|
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p,
|
||||||
|
L->base+1, CCF_ARG(2));
|
||||||
|
} else {
|
||||||
|
lj_err_argtype(L, 2, "cdata");
|
||||||
|
}
|
||||||
|
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF);
|
||||||
|
lj_buf_putmem((SBuf *)sbx, p, len);
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_reserve)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||||
|
GCcdata *cd;
|
||||||
|
lj_buf_more((SBuf *)sbx, len);
|
||||||
|
ctype_loadffi(L);
|
||||||
|
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR);
|
||||||
|
*(void **)cdataptr(cd) = sbx->w;
|
||||||
|
setcdataV(L, L->top++, cd);
|
||||||
|
setintV(L->top++, sbufleft(sbx));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_commit)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF);
|
||||||
|
if (len > sbufleft(sbx)) lj_err_arg(L, 2, LJ_ERR_NUMRNG);
|
||||||
|
sbx->w += len;
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_ref)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
GCcdata *cd;
|
||||||
|
ctype_loadffi(L);
|
||||||
|
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR);
|
||||||
|
*(void **)cdataptr(cd) = sbx->r;
|
||||||
|
setcdataV(L, L->top++, cd);
|
||||||
|
setintV(L->top++, sbufxlen(sbx));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_encode)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
cTValue *o = lj_lib_checkany(L, 2);
|
||||||
|
lj_serialize_put(sbx, o);
|
||||||
|
lj_gc_check(L);
|
||||||
|
L->top = L->base+1; /* Chain buffer object. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method_decode)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobufw(L);
|
||||||
|
setnilV(L->top++);
|
||||||
|
lj_serialize_get(sbx, L->top-1);
|
||||||
|
lj_gc_check(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method___gc)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
lj_bufx_free(G(L), sbx);
|
||||||
|
lj_bufx_init(L, sbx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method___tostring)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
setstrV(L, L->top-1, lj_str_new(L, sbx->r, sbufxlen(sbx)));
|
||||||
|
lj_gc_check(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_method___len)
|
||||||
|
{
|
||||||
|
SBufExt *sbx = buffer_tobuf(L);
|
||||||
|
setintV(L->top-1, (int32_t)sbufxlen(sbx));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LJLIB_PUSH("buffer") LJLIB_SET(__metatable)
|
||||||
|
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
#define LJLIB_MODULE_buffer
|
#define LJLIB_MODULE_buffer
|
||||||
|
|
||||||
|
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
|
||||||
|
|
||||||
|
LJLIB_CF(buffer_new)
|
||||||
|
{
|
||||||
|
MSize sz = L->base == L->top ? 0u :
|
||||||
|
(MSize)lj_lib_checkintrange(L, 1, 0, LJ_MAX_BUF);
|
||||||
|
GCtab *env = tabref(curr_func(L)->c.env);
|
||||||
|
GCudata *ud = lj_udata_new(L, sizeof(SBufExt), env);
|
||||||
|
SBufExt *sbx = (SBufExt *)uddata(ud);
|
||||||
|
ud->udtype = UDTYPE_BUFFER;
|
||||||
|
/* NOBARRIER: The GCudata is new (marked white). */
|
||||||
|
setgcref(ud->metatable, obj2gco(env));
|
||||||
|
setudataV(L, L->top++, ud);
|
||||||
|
lj_bufx_init(L, sbx);
|
||||||
|
if (sz > 0) lj_buf_need2((SBuf *)sbx, sz);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
LJLIB_CF(buffer_encode)
|
LJLIB_CF(buffer_encode)
|
||||||
{
|
{
|
||||||
cTValue *o = lj_lib_checkany(L, 1);
|
cTValue *o = lj_lib_checkany(L, 1);
|
||||||
@ -35,13 +307,14 @@ LJLIB_CF(buffer_encode)
|
|||||||
|
|
||||||
LJLIB_CF(buffer_decode)
|
LJLIB_CF(buffer_decode)
|
||||||
{
|
{
|
||||||
GCstr *str = lj_lib_checkstr(L, 1);
|
GCstr *str = lj_lib_checkstrx(L, 1);
|
||||||
SBufExt sbx;
|
SBufExt sbx;
|
||||||
lj_bufx_init_cow(L, &sbx, strdata(str), str->len);
|
lj_bufx_init_cow(L, &sbx, strdata(str), str->len);
|
||||||
/* No need to set sbx.cowref here. */
|
/* No need to set sbx.cowref here. */
|
||||||
setnilV(L->top++);
|
setnilV(L->top++);
|
||||||
lj_serialize_get(&sbx, L->top-1);
|
lj_serialize_get(&sbx, L->top-1);
|
||||||
lj_gc_check(L);
|
lj_gc_check(L);
|
||||||
|
if (sbx.r != sbx.w) lj_err_caller(L, LJ_ERR_BUFFER_LEFTOV);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +324,9 @@ LJLIB_CF(buffer_decode)
|
|||||||
|
|
||||||
int luaopen_string_buffer(lua_State *L)
|
int luaopen_string_buffer(lua_State *L)
|
||||||
{
|
{
|
||||||
|
LJ_LIB_REG(L, NULL, buffer_method);
|
||||||
|
lua_getfield(L, -1, "__tostring");
|
||||||
|
lua_setfield(L, -2, "tostring");
|
||||||
LJ_LIB_REG(L, NULL, buffer);
|
LJ_LIB_REG(L, NULL, buffer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#if LJ_HASJIT
|
#if LJ_HASJIT
|
||||||
|
|
||||||
#include "lj_gc.h"
|
#include "lj_gc.h"
|
||||||
|
#include "lj_buf.h"
|
||||||
#include "lj_str.h"
|
#include "lj_str.h"
|
||||||
#include "lj_tab.h"
|
#include "lj_tab.h"
|
||||||
#include "lj_frame.h"
|
#include "lj_frame.h"
|
||||||
|
@ -58,6 +58,10 @@ typedef struct SBufExt {
|
|||||||
(lj_assertG_(G(sbufL(sb)), sbufisext(sb), "not an SBufExt"), (SBufExt *)(sb))
|
(lj_assertG_(G(sbufL(sb)), sbufisext(sb), "not an SBufExt"), (SBufExt *)(sb))
|
||||||
#define setsbufflag(sb, flag) (setmrefu((sb)->L, (flag)))
|
#define setsbufflag(sb, flag) (setmrefu((sb)->L, (flag)))
|
||||||
|
|
||||||
|
#define tvisbuf(o) \
|
||||||
|
(LJ_HASBUFFER && tvisudata(o) && udataV(o)->udtype == UDTYPE_BUFFER)
|
||||||
|
#define bufV(o) check_exp(tvisbuf(o), ((SBufExt *)uddata(udataV(o))))
|
||||||
|
|
||||||
/* Buffer management */
|
/* Buffer management */
|
||||||
LJ_FUNC char *LJ_FASTCALL lj_buf_need2(SBuf *sb, MSize sz);
|
LJ_FUNC char *LJ_FASTCALL lj_buf_need2(SBuf *sb, MSize sz);
|
||||||
LJ_FUNC char *LJ_FASTCALL lj_buf_more2(SBuf *sb, MSize sz);
|
LJ_FUNC char *LJ_FASTCALL lj_buf_more2(SBuf *sb, MSize sz);
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#if LJ_HASFFI
|
#if LJ_HASFFI
|
||||||
|
|
||||||
#include "lj_err.h"
|
#include "lj_err.h"
|
||||||
|
#include "lj_buf.h"
|
||||||
#include "lj_tab.h"
|
#include "lj_tab.h"
|
||||||
#include "lj_ctype.h"
|
#include "lj_ctype.h"
|
||||||
#include "lj_cdata.h"
|
#include "lj_cdata.h"
|
||||||
@ -621,6 +622,8 @@ void lj_cconv_ct_tv(CTState *cts, CType *d,
|
|||||||
tmpptr = uddata(ud);
|
tmpptr = uddata(ud);
|
||||||
if (ud->udtype == UDTYPE_IO_FILE)
|
if (ud->udtype == UDTYPE_IO_FILE)
|
||||||
tmpptr = *(void **)tmpptr;
|
tmpptr = *(void **)tmpptr;
|
||||||
|
else if (ud->udtype == UDTYPE_BUFFER)
|
||||||
|
tmpptr = ((SBufExt *)tmpptr)->r;
|
||||||
} else if (tvislightud(o)) {
|
} else if (tvislightud(o)) {
|
||||||
tmpptr = lightudV(cts->g, o);
|
tmpptr = lightudV(cts->g, o);
|
||||||
} else if (tvisfunc(o)) {
|
} else if (tvisfunc(o)) {
|
||||||
|
@ -616,10 +616,12 @@ static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval)
|
|||||||
sp = lj_ir_kptr(J, NULL);
|
sp = lj_ir_kptr(J, NULL);
|
||||||
} else if (tref_isudata(sp)) {
|
} else if (tref_isudata(sp)) {
|
||||||
GCudata *ud = udataV(sval);
|
GCudata *ud = udataV(sval);
|
||||||
if (ud->udtype == UDTYPE_IO_FILE) {
|
if (ud->udtype == UDTYPE_IO_FILE || ud->udtype == UDTYPE_BUFFER) {
|
||||||
TRef tr = emitir(IRT(IR_FLOAD, IRT_U8), sp, IRFL_UDATA_UDTYPE);
|
TRef tr = emitir(IRT(IR_FLOAD, IRT_U8), sp, IRFL_UDATA_UDTYPE);
|
||||||
emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE));
|
emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, ud->udtype));
|
||||||
sp = emitir(IRT(IR_FLOAD, IRT_PTR), sp, IRFL_UDATA_FILE);
|
sp = emitir(IRT(IR_FLOAD, IRT_PTR), sp,
|
||||||
|
ud->udtype == UDTYPE_IO_FILE ? IRFL_UDATA_FILE :
|
||||||
|
IRFL_UDATA_BUF_R);
|
||||||
} else {
|
} else {
|
||||||
sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCudata)));
|
sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCudata)));
|
||||||
}
|
}
|
||||||
|
@ -298,6 +298,7 @@ typedef struct CTState {
|
|||||||
_(P_VOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_VOID) \
|
_(P_VOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_VOID) \
|
||||||
_(P_CVOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CVOID) \
|
_(P_CVOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CVOID) \
|
||||||
_(P_CCHAR, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CCHAR) \
|
_(P_CCHAR, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CCHAR) \
|
||||||
|
_(P_UINT8, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_UINT8) \
|
||||||
_(A_CCHAR, -1, CT_ARRAY, CTF_CONST|CTALIGN(0)|CTID_CCHAR) \
|
_(A_CCHAR, -1, CT_ARRAY, CTF_CONST|CTALIGN(0)|CTID_CCHAR) \
|
||||||
_(CTYPEID, 4, CT_ENUM, CTALIGN(2)|CTID_INT32) \
|
_(CTYPEID, 4, CT_ENUM, CTALIGN(2)|CTID_INT32) \
|
||||||
CTTYDEFP(_) \
|
CTTYDEFP(_) \
|
||||||
|
@ -67,6 +67,7 @@ ERRDEF(PROTMT, "cannot change a protected metatable")
|
|||||||
ERRDEF(UNPACK, "too many results to unpack")
|
ERRDEF(UNPACK, "too many results to unpack")
|
||||||
ERRDEF(RDRSTR, "reader function must return a string")
|
ERRDEF(RDRSTR, "reader function must return a string")
|
||||||
ERRDEF(PRTOSTR, LUA_QL("tostring") " must return a string to " LUA_QL("print"))
|
ERRDEF(PRTOSTR, LUA_QL("tostring") " must return a string to " LUA_QL("print"))
|
||||||
|
ERRDEF(NUMRNG, "number out of range")
|
||||||
ERRDEF(IDXRNG, "index out of range")
|
ERRDEF(IDXRNG, "index out of range")
|
||||||
ERRDEF(BASERNG, "base out of range")
|
ERRDEF(BASERNG, "base out of range")
|
||||||
ERRDEF(LVLRNG, "level out of range")
|
ERRDEF(LVLRNG, "level out of range")
|
||||||
|
@ -65,6 +65,12 @@ static void gc_mark(global_State *g, GCobj *o)
|
|||||||
gray2black(o); /* Userdata are never gray. */
|
gray2black(o); /* Userdata are never gray. */
|
||||||
if (mt) gc_markobj(g, mt);
|
if (mt) gc_markobj(g, mt);
|
||||||
gc_markobj(g, tabref(gco2ud(o)->env));
|
gc_markobj(g, tabref(gco2ud(o)->env));
|
||||||
|
if (LJ_HASBUFFER && gco2ud(o)->udtype == UDTYPE_BUFFER) {
|
||||||
|
SBufExt *sbx = (SBufExt *)uddata(gco2ud(o));
|
||||||
|
if (sbufiscow(sbx) && gcref(sbx->cowref) != NULL) {
|
||||||
|
gc_markobj(g, gcref(sbx->cowref));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
|
} else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
|
||||||
GCupval *uv = gco2uv(o);
|
GCupval *uv = gco2uv(o);
|
||||||
gc_marktv(g, uvval(uv));
|
gc_marktv(g, uvval(uv));
|
||||||
|
@ -204,6 +204,7 @@ IRFPMDEF(FPMENUM)
|
|||||||
_(UDATA_META, offsetof(GCudata, metatable)) \
|
_(UDATA_META, offsetof(GCudata, metatable)) \
|
||||||
_(UDATA_UDTYPE, offsetof(GCudata, udtype)) \
|
_(UDATA_UDTYPE, offsetof(GCudata, udtype)) \
|
||||||
_(UDATA_FILE, sizeof(GCudata)) \
|
_(UDATA_FILE, sizeof(GCudata)) \
|
||||||
|
_(UDATA_BUF_R, sizeof(GCudata) + offsetof(SBufExt, r)) \
|
||||||
_(CDATA_CTYPEID, offsetof(GCcdata, ctypeid)) \
|
_(CDATA_CTYPEID, offsetof(GCcdata, ctypeid)) \
|
||||||
_(CDATA_PTR, sizeof(GCcdata)) \
|
_(CDATA_PTR, sizeof(GCcdata)) \
|
||||||
_(CDATA_INT, sizeof(GCcdata)) \
|
_(CDATA_INT, sizeof(GCcdata)) \
|
||||||
|
54
src/lj_lib.c
54
src/lj_lib.c
@ -16,6 +16,9 @@
|
|||||||
#include "lj_func.h"
|
#include "lj_func.h"
|
||||||
#include "lj_bc.h"
|
#include "lj_bc.h"
|
||||||
#include "lj_dispatch.h"
|
#include "lj_dispatch.h"
|
||||||
|
#if LJ_HASFFI
|
||||||
|
#include "lj_ctype.h"
|
||||||
|
#endif
|
||||||
#include "lj_vm.h"
|
#include "lj_vm.h"
|
||||||
#include "lj_strscan.h"
|
#include "lj_strscan.h"
|
||||||
#include "lj_strfmt.h"
|
#include "lj_strfmt.h"
|
||||||
@ -301,3 +304,54 @@ int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst)
|
|||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -- Strict type checks -------------------------------------------------- */
|
||||||
|
|
||||||
|
/* The following type checks do not coerce between strings and numbers.
|
||||||
|
** And they handle plain int64_t/uint64_t FFI numbers, too.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if LJ_HASBUFFER
|
||||||
|
GCstr *lj_lib_checkstrx(lua_State *L, int narg)
|
||||||
|
{
|
||||||
|
TValue *o = L->base + narg-1;
|
||||||
|
if (!(o < L->top && tvisstr(o))) lj_err_argt(L, narg, LUA_TSTRING);
|
||||||
|
return strV(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t lj_lib_checkintrange(lua_State *L, int narg, int32_t a, int32_t b)
|
||||||
|
{
|
||||||
|
TValue *o = L->base + narg-1;
|
||||||
|
lj_assertL(b >= 0, "expected range must be non-negative");
|
||||||
|
if (o < L->top) {
|
||||||
|
if (LJ_LIKELY(tvisint(o))) {
|
||||||
|
int32_t i = intV(o);
|
||||||
|
if (i >= a && i <= b) return i;
|
||||||
|
} else if (LJ_LIKELY(tvisnum(o))) {
|
||||||
|
/* For performance reasons, this doesn't check for integerness or
|
||||||
|
** integer overflow. Overflow detection still works, since all FPUs
|
||||||
|
** return either MININT or MAXINT, which is then out of range.
|
||||||
|
*/
|
||||||
|
int32_t i = (int32_t)numV(o);
|
||||||
|
if (i >= a && i <= b) return i;
|
||||||
|
#if LJ_HASFFI
|
||||||
|
} else if (tviscdata(o)) {
|
||||||
|
GCcdata *cd = cdataV(o);
|
||||||
|
if (cd->ctypeid == CTID_INT64) {
|
||||||
|
int64_t i = *(int64_t *)cdataptr(cd);
|
||||||
|
if (i >= (int64_t)a && i <= (int64_t)b) return (int32_t)i;
|
||||||
|
} else if (cd->ctypeid == CTID_UINT64) {
|
||||||
|
uint64_t i = *(uint64_t *)cdataptr(cd);
|
||||||
|
if ((a < 0 || i >= (uint64_t)a) && i <= (uint64_t)b) return (int32_t)i;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
goto badtype;
|
||||||
|
}
|
||||||
|
lj_err_arg(L, narg, LJ_ERR_NUMRNG);
|
||||||
|
}
|
||||||
|
badtype:
|
||||||
|
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||||
|
return 0; /* unreachable */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@ -46,6 +46,12 @@ LJ_FUNC GCtab *lj_lib_checktab(lua_State *L, int narg);
|
|||||||
LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg);
|
LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg);
|
||||||
LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst);
|
LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst);
|
||||||
|
|
||||||
|
#if LJ_HASBUFFER
|
||||||
|
LJ_FUNC GCstr *lj_lib_checkstrx(lua_State *L, int narg);
|
||||||
|
LJ_FUNC int32_t lj_lib_checkintrange(lua_State *L, int narg,
|
||||||
|
int32_t a, int32_t b);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Avoid including lj_frame.h. */
|
/* Avoid including lj_frame.h. */
|
||||||
#if LJ_GC64
|
#if LJ_GC64
|
||||||
#define lj_lib_upvalue(L, n) \
|
#define lj_lib_upvalue(L, n) \
|
||||||
|
@ -240,8 +240,8 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left)
|
|||||||
int fromc = 0;
|
int fromc = 0;
|
||||||
if (left < 0) { left = -left; fromc = 1; }
|
if (left < 0) { left = -left; fromc = 1; }
|
||||||
do {
|
do {
|
||||||
if (!(tvisstr(top) || tvisnumber(top)) ||
|
if (!(tvisstr(top) || tvisnumber(top) || tvisbuf(top)) ||
|
||||||
!(tvisstr(top-1) || tvisnumber(top-1))) {
|
!(tvisstr(top-1) || tvisnumber(top-1) || tvisbuf(top-1))) {
|
||||||
cTValue *mo = lj_meta_lookup(L, top-1, MM_concat);
|
cTValue *mo = lj_meta_lookup(L, top-1, MM_concat);
|
||||||
if (tvisnil(mo)) {
|
if (tvisnil(mo)) {
|
||||||
mo = lj_meta_lookup(L, top, MM_concat);
|
mo = lj_meta_lookup(L, top, MM_concat);
|
||||||
@ -277,10 +277,12 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left)
|
|||||||
** next step: [...][CAT stack ............]
|
** next step: [...][CAT stack ............]
|
||||||
*/
|
*/
|
||||||
TValue *e, *o = top;
|
TValue *e, *o = top;
|
||||||
uint64_t tlen = tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM;
|
uint64_t tlen = tvisstr(o) ? strV(o)->len :
|
||||||
|
tvisbuf(o) ? sbufxlen(bufV(o)) : STRFMT_MAXBUF_NUM;
|
||||||
SBuf *sb;
|
SBuf *sb;
|
||||||
do {
|
do {
|
||||||
o--; tlen += tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM;
|
o--; tlen += tvisstr(o) ? strV(o)->len :
|
||||||
|
tvisbuf(o) ? sbufxlen(bufV(o)) : STRFMT_MAXBUF_NUM;
|
||||||
} while (--left > 0 && (tvisstr(o-1) || tvisnumber(o-1)));
|
} while (--left > 0 && (tvisstr(o-1) || tvisnumber(o-1)));
|
||||||
if (tlen >= LJ_MAX_STR) lj_err_msg(L, LJ_ERR_STROV);
|
if (tlen >= LJ_MAX_STR) lj_err_msg(L, LJ_ERR_STROV);
|
||||||
sb = lj_buf_tmp_(L);
|
sb = lj_buf_tmp_(L);
|
||||||
@ -290,6 +292,9 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left)
|
|||||||
GCstr *s = strV(o);
|
GCstr *s = strV(o);
|
||||||
MSize len = s->len;
|
MSize len = s->len;
|
||||||
lj_buf_putmem(sb, strdata(s), len);
|
lj_buf_putmem(sb, strdata(s), len);
|
||||||
|
} else if (tvisbuf(o)) {
|
||||||
|
SBufExt *sbx = bufV(o);
|
||||||
|
lj_buf_putmem(sb, sbx->r, sbufxlen(sbx));
|
||||||
} else if (tvisint(o)) {
|
} else if (tvisint(o)) {
|
||||||
lj_strfmt_putint(sb, intV(o));
|
lj_strfmt_putint(sb, intV(o));
|
||||||
} else {
|
} else {
|
||||||
|
@ -332,6 +332,7 @@ enum {
|
|||||||
UDTYPE_USERDATA, /* Regular userdata. */
|
UDTYPE_USERDATA, /* Regular userdata. */
|
||||||
UDTYPE_IO_FILE, /* I/O library FILE. */
|
UDTYPE_IO_FILE, /* I/O library FILE. */
|
||||||
UDTYPE_FFI_CLIB, /* FFI C library namespace. */
|
UDTYPE_FFI_CLIB, /* FFI C library namespace. */
|
||||||
|
UDTYPE_BUFFER, /* String buffer. */
|
||||||
UDTYPE__MAX
|
UDTYPE__MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -346,10 +346,7 @@ SBufExt * LJ_FASTCALL lj_serialize_put(SBufExt *sbx, cTValue *o)
|
|||||||
|
|
||||||
SBufExt * LJ_FASTCALL lj_serialize_get(SBufExt *sbx, TValue *o)
|
SBufExt * LJ_FASTCALL lj_serialize_get(SBufExt *sbx, TValue *o)
|
||||||
{
|
{
|
||||||
char *r = serialize_get(sbx->r, sbx, o);
|
sbx->r = serialize_get(sbx->r, sbx, o);
|
||||||
if (r != sbx->w)
|
|
||||||
lj_err_caller(sbufL(sbx), LJ_ERR_BUFFER_LEFTOV);
|
|
||||||
sbx->r = r;
|
|
||||||
return sbx;
|
return sbx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +164,10 @@ const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp)
|
|||||||
if (tvisstr(o)) {
|
if (tvisstr(o)) {
|
||||||
*lenp = strV(o)->len;
|
*lenp = strV(o)->len;
|
||||||
return strVdata(o);
|
return strVdata(o);
|
||||||
|
} else if (tvisbuf(o)) {
|
||||||
|
SBufExt *sbx = bufV(o);
|
||||||
|
*lenp = sbufxlen(sbx);
|
||||||
|
return sbx->r;
|
||||||
} else if (tvisint(o)) {
|
} else if (tvisint(o)) {
|
||||||
sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o));
|
sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o));
|
||||||
} else if (tvisnum(o)) {
|
} else if (tvisnum(o)) {
|
||||||
@ -421,6 +425,10 @@ int lj_strfmt_putarg(lua_State *L, SBuf *sb, int arg, int retry)
|
|||||||
if (LJ_LIKELY(tvisstr(o))) {
|
if (LJ_LIKELY(tvisstr(o))) {
|
||||||
len = strV(o)->len;
|
len = strV(o)->len;
|
||||||
s = strVdata(o);
|
s = strVdata(o);
|
||||||
|
} else if (tvisbuf(o)) {
|
||||||
|
SBufExt *sbx = bufV(o);
|
||||||
|
len = sbufxlen(sbx);
|
||||||
|
s = sbx->r;
|
||||||
} else {
|
} else {
|
||||||
GCstr *str = lj_strfmt_obj(L, o);
|
GCstr *str = lj_strfmt_obj(L, o);
|
||||||
len = str->len;
|
len = str->len;
|
||||||
|
Loading…
Reference in New Issue
Block a user