Skip to content

Commit 87a99cf

Browse files
committed
Add initial support for Lua.Buffer including support for buffer.addChar(), lua.initBuffer(), buffer.pushResult()
1 parent e02a2f2 commit 87a99cf

File tree

3 files changed

+115
-39
lines changed

3 files changed

+115
-39
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ you.
8686
| API | Support |
8787
|-----------------------------|---------------------------------------|
8888
| Lua C API (`lua_*`) | 🎉 100% coverage<sup>†</sup> (92/92) |
89-
| Auxilary Library (`luaL_*`) | 70% coverage (35/48) |
89+
| Auxilary Library (`luaL_*`) | 80% coverage (40/48) |
9090
| LuaJIT Extensions | *No plans to implement.* |
9191

9292
*†: Coroutine yield/resume is not yet part of the public `zig-luajit` Zig API, see [#6][ISSUE-6].*
@@ -218,19 +218,19 @@ The `zig-luajit` project has not yet reached the 1.0 release, the API is subject
218218

219219
| C Type Definition | Available in `zig-luajit` |
220220
|----------------------------|-------------------------------------|
221-
| `luaL_Buffer` ||
221+
| `luaL_Buffer` | ☑️ `Lua.Buffer` |
222222
| `luaL_Reg` | ☑️ `Lua.Reg` and `Lua.RegEnd` |
223223

224224
| C API Symbol | Available in `zig-luajit` |
225225
|----------------------------|-------------------------------------|
226-
| `luaL_addchar` ||
226+
| `luaL_addchar` | ☑️ `buffer.addChar()`|
227227
| `luaL_addlstring` ||
228228
| `luaL_addsize` ||
229229
| `luaL_addstring` ||
230230
| `luaL_addvalue` ||
231231
| `luaL_argcheck` | ☑️📢 `lua.checkArgument()` |
232232
| `luaL_argerror` | ☑️📢 `lua.raiseErrorArgument()` |
233-
| `luaL_buffinit` ||
233+
| `luaL_buffinit` | ☑️📢 `lua.initBuffer()`|
234234
| `luaL_callmeta` | ☑️ `lua.callMeta()`|
235235
| `luaL_checkany` | ☑️ `lua.checkAny()`|
236236
| `luaL_checkinteger` | ☑️ `lua.checkInteger()` |
@@ -262,7 +262,7 @@ The `zig-luajit` project has not yet reached the 1.0 release, the API is subject
262262
| `luaL_optnumber` | ☑️📢 `lua.checkNumberOptional()` |
263263
| `luaL_optstring` | ☑️📢 `lua.checkStringOptional()` |
264264
| `luaL_prepbuffer` ||
265-
| `luaL_pushresult` ||
265+
| `luaL_pushresult` | ☑️ `buffer.pushResult()` |
266266
| `luaL_ref` | ☑️ `lua.ref()` |
267267
| `luaL_unref` | ☑️ `lua.unref()` |
268268
| `luaL_register` | ☑️📢 `lua.registerLibrary()` |

src/lual_api.zig

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@
33

44
// This file contains brainstorming and draft translations of the C API to Lua.
55

6-
/// Adds the character c to the given buffer.
7-
///
8-
/// From: `void luaL_addchar(luaL_Buffer *B, char c);`
9-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_addchar
10-
/// Stack Behavior: `[-0, +0, m]`
11-
pub fn addChar(buffer: *LuaBuffer, char: u8) void;
12-
136
/// Adds the string pointed to by `s` with length `l` to the buffer `B`. The string may contain embedded zeros.
147
///
158
/// From: `void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l);`
@@ -41,26 +34,6 @@ pub fn addString(buffer: *Buffer, s: [*:0]const u8) void;
4134
/// Stack Behavior: `[-1, +0, m]`
4235
pub fn addValue(buffer: *Buffer) void;
4336

44-
/// Type for a string buffer. A string buffer allows building Lua strings piecemeal.
45-
///
46-
/// Pattern of use:
47-
/// 1. Declare a variable of type Buffer
48-
/// 2. Initialize with bufferInit()
49-
/// 3. Add string pieces using addX() functions
50-
/// 4. Finish by calling pushResult()
51-
///
52-
/// From: `typedef struct luaL_Buffer luaL_Buffer;`
53-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_Buffer
54-
pub const Buffer = opaque {};
55-
56-
/// Initializes a Lua buffer. This function does not allocate any space;
57-
/// the buffer must be declared as a variable.
58-
///
59-
/// From: `void luaL_buffinit(lua_State *L, luaL_Buffer *B);`
60-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_buffinit
61-
/// Stack Behavior: `[-0, +0, -]`
62-
pub fn bufInit(lua: *Lua, buffer: *Buffer) void;
63-
6437
/// Creates a new Lua state using the standard C realloc function for memory allocation and sets a default
6538
/// panic function that prints an error message to the standard error output in case of fatal errors.
6639
///
@@ -78,13 +51,6 @@ pub fn newState() ?*Lua;
7851
/// Stack Behavior: `[-0, +0, -]`
7952
pub fn prepBuffer(buffer: *Buffer) [*]u8;
8053

81-
/// Finishes the use of buffer B leaving the final string on the top of the stack.
82-
///
83-
/// From: `void luaL_pushresult(luaL_Buffer *B);`
84-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_pushresult
85-
/// Stack Behavior: `[-?, +1, m]`
86-
pub fn pushResult(buffer: *LuaBuffer) void;
87-
8854
/// Generates an error with a message like "location: bad argument narg to 'func' (tname expected, got rt)",
8955
/// where location is produced by luaL_where, func is the name of the current function,
9056
/// and rt is the type name of the actual argument.

src/root.zig

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,74 @@ pub const Lua = opaque {
24522452
unreachable;
24532453
}
24542454
}
2455+
2456+
/// Type for a string buffer. A string buffer allows building Lua strings piecemeal.
2457+
///
2458+
/// Pattern of use:
2459+
/// 1. Declare a variable of type Buffer
2460+
/// 2. Initialize with bufferInit()
2461+
/// 3. Add string pieces using addX() functions
2462+
/// 4. Finish by calling pushResult()
2463+
///
2464+
/// From: `typedef struct luaL_Buffer luaL_Buffer;`
2465+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_Buffer
2466+
pub const Buffer = extern struct {
2467+
const BufferSize: usize = @intCast(c.LUAL_BUFFERSIZE);
2468+
p: ?[*]u8 = null,
2469+
lvl: c_int = 0,
2470+
L: ?*Lua = null,
2471+
buffer: [BufferSize]u8 = undefined,
2472+
2473+
/// Adds the character c to the given buffer.
2474+
///
2475+
/// From: `void luaL_addchar(luaL_Buffer *B, char c);`
2476+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_addchar
2477+
/// Stack Behavior: `[-0, +0, m]`
2478+
pub fn addChar(buffer: *Buffer, char: u8) void {
2479+
assert(buffer.p != null and buffer.L != null); // You must use `Lua.initBuffer(&Lua.Buffer)` before calling `Lua.Buffer.addChar()`.
2480+
2481+
if (buffer.p) |ptr| {
2482+
// if the buffer is out of capacity, we will want to call `prepbuffer` to get more space and change the pointer.
2483+
var ptr_copy = ptr;
2484+
2485+
if (@intFromPtr(ptr) >= @intFromPtr(&buffer.buffer) + c.LUAL_BUFFERSIZE) {
2486+
const extra = c.luaL_prepbuffer(@ptrCast(buffer));
2487+
assert(extra != null);
2488+
ptr_copy = extra;
2489+
}
2490+
2491+
// We can assert buffer.p is non-null here since prepbuffer guarantees it
2492+
ptr_copy[0] = char;
2493+
buffer.p = ptr_copy + 1;
2494+
} else {
2495+
std.debug.panic(
2496+
"Failed to add character '{c}' to a buffer: the buffer is not initialized, `buffer.p` is null.\n",
2497+
.{char},
2498+
);
2499+
}
2500+
}
2501+
2502+
/// Finishes the use of the buffer leaving the final string on the top of the stack.
2503+
///
2504+
/// From: `void luaL_pushresult(luaL_Buffer *B);`
2505+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_pushresult
2506+
/// Stack Behavior: `[-?, +1, m]`
2507+
pub fn pushResult(buffer: *Lua.Buffer) void {
2508+
assert(buffer.p != null and buffer.L != null); // You must use `Lua.initBuffer(&Lua.Buffer)` before calling `Lua.Buffer.pushResult()`.
2509+
2510+
return c.luaL_pushresult(@ptrCast(buffer));
2511+
}
2512+
};
2513+
2514+
/// Initializes a Lua buffer. This function does not allocate any space;
2515+
/// the buffer must be declared as a variable.
2516+
///
2517+
/// From: `void luaL_buffinit(lua_State *L, luaL_Buffer *B);`
2518+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_buffinit
2519+
/// Stack Behavior: `[-0, +0, -]`
2520+
pub fn initBuffer(lua: *Lua, buffer: *Lua.Buffer) void {
2521+
return c.luaL_buffinit(asState(lua), @ptrCast(buffer));
2522+
}
24552523
};
24562524

24572525
test "Lua can be initialized with an allocator" {
@@ -4679,3 +4747,45 @@ test "callMeta() should do nothing when it is not defined" {
46794747
try std.testing.expect(lua.isTable(-1));
46804748
try std.testing.expectEqual(1, lua.getTop());
46814749
}
4750+
4751+
test "Buffer should be able to build character by character" {
4752+
const lua = try Lua.init(std.testing.allocator);
4753+
defer lua.deinit();
4754+
4755+
var b: Lua.Buffer = .{};
4756+
lua.initBuffer(&b);
4757+
b.addChar('H');
4758+
b.addChar('e');
4759+
b.addChar('l');
4760+
b.addChar('l');
4761+
b.addChar('o');
4762+
b.addChar(',');
4763+
b.addChar(' ');
4764+
b.addChar('w');
4765+
b.addChar('o');
4766+
b.addChar('r');
4767+
b.addChar('l');
4768+
b.addChar('d');
4769+
b.addChar('!');
4770+
b.pushResult();
4771+
4772+
try std.testing.expectEqual(1, lua.getTop());
4773+
try std.testing.expect(lua.isString(-1));
4774+
try std.testing.expectEqualStrings("Hello, world!", try lua.toLString(-1));
4775+
}
4776+
4777+
test "Buffer should handle very long string" {
4778+
const lua = try Lua.init(std.testing.allocator);
4779+
defer lua.deinit();
4780+
4781+
var b: Lua.Buffer = .{};
4782+
lua.initBuffer(&b);
4783+
for (0..256_000) |i| {
4784+
b.addChar('0' + @as(u8, @intCast((i % 10))));
4785+
}
4786+
b.pushResult();
4787+
4788+
try std.testing.expectEqual(1, lua.getTop());
4789+
try std.testing.expect(lua.isString(-1));
4790+
try std.testing.expectEqualStrings("0123456789" ** 25_600, try lua.toLString(-1));
4791+
}

0 commit comments

Comments
 (0)