diff --git a/Aegisub.sln b/Aegisub.sln
index 22c2ce5f3..8a6d6112b 100644
--- a/Aegisub.sln
+++ b/Aegisub.sln
@@ -28,6 +28,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libresrc", "build\libresrc\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lua51", "build\lua51\lua51.vcxproj", "{5391A8B1-9C70-4DC4-92AD-D3E34C6B803F}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luabins", "build\luabins\luabins.vcxproj", "{A7A30702-8162-4E1A-A010-EF51B590C121}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "respack", "build\respack\respack.vcxproj", "{08AF2BCC-FCDD-4F0E-8B41-59A6E634F2E8}"
ProjectSection(ProjectDependencies) = postProject
{FB8E8D19-A4D6-4181-943C-282075F49B41} = {FB8E8D19-A4D6-4181-943C-282075F49B41}
@@ -223,6 +225,20 @@ Global
{5391A8B1-9C70-4DC4-92AD-D3E34C6B803F}.Release|x64.Build.0 = Release|x64
{5391A8B1-9C70-4DC4-92AD-D3E34C6B803F}.Release-MinDep|Win32.ActiveCfg = Release|Win32
{5391A8B1-9C70-4DC4-92AD-D3E34C6B803F}.Release-MinDep|x64.ActiveCfg = Release|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.Build.0 = Debug|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x64.ActiveCfg = Debug|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x64.Build.0 = Debug|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug-MinDep|Win32.ActiveCfg = Debug|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug-MinDep|x64.ActiveCfg = Debug|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug-Tests|Win32.ActiveCfg = Debug|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug-Tests|x64.ActiveCfg = Debug|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.ActiveCfg = Release|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.Build.0 = Release|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x64.ActiveCfg = Release|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x64.Build.0 = Release|x64
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release-MinDep|Win32.ActiveCfg = Release|Win32
+ {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release-MinDep|x64.ActiveCfg = Release|x64
{08AF2BCC-FCDD-4F0E-8B41-59A6E634F2E8}.Debug|Win32.ActiveCfg = Debug|Win32
{08AF2BCC-FCDD-4F0E-8B41-59A6E634F2E8}.Debug|Win32.Build.0 = Debug|Win32
{08AF2BCC-FCDD-4F0E-8B41-59A6E634F2E8}.Debug|x64.ActiveCfg = Debug|x64
diff --git a/Makefile b/Makefile
index 828f49666..e4fa04a6b 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ include Makefile.inc
SUBDIRS += \
vendor/lua \
vendor/universalchardet \
+ vendor/luabins \
libaegisub \
tools \
src \
diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj
index b1bac423c..56aa2e069 100644
--- a/build/Aegisub/Aegisub.vcxproj
+++ b/build/Aegisub/Aegisub.vcxproj
@@ -81,6 +81,9 @@
{5391a8b1-9c70-4dc4-92ad-d3e34c6b803f}
+
+ {A7A30702-8162-4E1A-A010-EF51B590C121}
+
{7b56955d-5162-4698-aa5b-47484edc8783}
diff --git a/build/luabins/luabins.vcxproj b/build/luabins/luabins.vcxproj
new file mode 100644
index 000000000..c0e7f4093
--- /dev/null
+++ b/build/luabins/luabins.vcxproj
@@ -0,0 +1,44 @@
+
+
+
+ {A7A30702-8162-4E1A-A010-EF51B590C121}
+ luabins
+
+
+
+
+ lib
+ ..\..\vendor\luabins\src\
+
+
+
+
+
+
+
+
+ LUABINS_LUABUILTASCPP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ $(SrcDir)..\..\lua51\src;%(AdditionalIncludeDirectories)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/luabins/luabins.vcxproj.filters b/build/luabins/luabins.vcxproj.filters
new file mode 100644
index 000000000..7a9b51b8e
--- /dev/null
+++ b/build/luabins/luabins.vcxproj.filters
@@ -0,0 +1,57 @@
+
+
+
+
+ {0A33FF05-970D-49a7-B722-73E8EA350084}
+
+
+ {2C50401A-5AC6-4630-B633-DFCC190306A8}
+
+
+
+
+ Source
+
+
+ Source
+
+
+ Source
+
+
+ Source
+
+
+ Source
+
+
+ Source
+
+
+ Source
+
+
+
+
+ Headers
+
+
+ Headers
+
+
+ Headers
+
+
+ Headers
+
+
+ Headers
+
+
+ Headers
+
+
+ Headers
+
+
+
diff --git a/libaegisub/include/libaegisub/ass/uuencode.h b/libaegisub/include/libaegisub/ass/uuencode.h
index fbc957c3d..b6b30e442 100644
--- a/libaegisub/include/libaegisub/ass/uuencode.h
+++ b/libaegisub/include/libaegisub/ass/uuencode.h
@@ -28,7 +28,7 @@ namespace agi { namespace ass {
/// Encode a blob of data, using ASS's nonstandard variant
template
-std::string UUEncode(RandomAccessRange const& data) {
+std::string UUEncode(RandomAccessRange const& data, bool insert_linebreaks=true) {
using std::begin;
using std::end;
@@ -51,7 +51,7 @@ std::string UUEncode(RandomAccessRange const& data) {
for (size_t i = 0; i < std::min(size - pos + 1, 4u); ++i) {
ret += dst[i] + 33;
- if (++written == 80 && pos + 3 < size) {
+ if (insert_linebreaks && ++written == 80 && pos + 3 < size) {
written = 0;
ret += "\r\n";
}
diff --git a/src/Makefile b/src/Makefile
index 919c55abc..77f91e1b6 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -14,6 +14,7 @@ LIBS := -L../libaegisub -laegisub $(LIBS)
LIBS += $(LIBS_GL) $(LIBS_PTHREAD) $(LIBS_WX) $(LIBS_FREETYPE)
LIBS += $(LIBS_FONTCONFIG) $(LIBS_FFTW3) $(LIBS_UCHARDET) $(LIBS_BOOST)
LIBS += $(LIBS_ICU) $(LIBS_LUA)
+LIBS += ../vendor/luabins/libluabins.a
ifeq (yes, $(BUILD_DARWIN))
SRC += osx_utils.mm retina_helper.mm
diff --git a/src/ass_dialogue.cpp b/src/ass_dialogue.cpp
index 9f578effa..80378172c 100644
--- a/src/ass_dialogue.cpp
+++ b/src/ass_dialogue.cpp
@@ -44,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
@@ -120,7 +121,28 @@ void AssDialogue::Parse(std::string const& raw) {
for (int& margin : Margin)
margin = mid(0, boost::lexical_cast(tkn.next_str()), 9999);
Effect = tkn.next_str_trim();
- Text = std::string(tkn.next_tok().begin(), str.end());
+
+ std::string text{tkn.next_tok().begin(), str.end()};
+
+ static const boost::regex extradata_test("^\\{(=\\d+)+\\}");
+ boost::match_results rematch;
+ if (boost::regex_search(text.begin(), text.end(), rematch, extradata_test)) {
+ std::string extradata_str = rematch.str(0);
+ text = rematch.suffix().str();
+
+ static const boost::regex idmatcher("=(\\d+)");
+ auto start = extradata_str.begin();
+ auto end = extradata_str.end();
+ std::vector ids;
+ while (boost::regex_search(start, end, rematch, idmatcher)) {
+ auto id = boost::lexical_cast(rematch.str(1));
+ ids.push_back(id);
+ start = rematch.suffix().first;
+ }
+ ExtradataIds = ids;
+ }
+
+ Text = text;
}
void append_int(std::string &str, int v) {
@@ -156,6 +178,16 @@ std::string AssDialogue::GetData(bool ssa) const {
for (auto margin : Margin)
append_int(str, margin);
append_unsafe_str(str, Effect);
+
+ if (ExtradataIds.get().size() > 0) {
+ str += "{";
+ for (auto id : ExtradataIds.get()) {
+ str += "=";
+ boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, id);
+ }
+ str += "}";
+ }
+
str += Text.get();
if (str.find('\n') != str.npos || str.find('\r') != str.npos) {
diff --git a/src/ass_dialogue.h b/src/ass_dialogue.h
index 2a1dd28b0..affa6809a 100644
--- a/src/ass_dialogue.h
+++ b/src/ass_dialogue.h
@@ -146,6 +146,8 @@ struct AssDialogueBase {
boost::flyweight Actor;
/// Effect name
boost::flyweight Effect;
+ /// IDs of extradata entries for line
+ boost::flyweight> ExtradataIds;
/// Raw text data
boost::flyweight Text;
};
@@ -183,3 +185,4 @@ public:
AssDialogue(std::string const& data);
~AssDialogue();
};
+
diff --git a/src/ass_entry.cpp b/src/ass_entry.cpp
index 30ec7f8dc..bdf486b0f 100644
--- a/src/ass_entry.cpp
+++ b/src/ass_entry.cpp
@@ -28,6 +28,7 @@ std::string const& AssEntry::GroupHeader(bool ssa) const {
"[Fonts]",
"[Graphics]",
"[Events]",
+ "[Aegisub Extradata]",
""
};
@@ -37,6 +38,7 @@ std::string const& AssEntry::GroupHeader(bool ssa) const {
"[Fonts]",
"[Graphics]",
"[Events]",
+ "[Aegisub Extradata]",
""
};
diff --git a/src/ass_entry.h b/src/ass_entry.h
index f03d10336..9556ef18a 100644
--- a/src/ass_entry.h
+++ b/src/ass_entry.h
@@ -43,6 +43,7 @@ enum class AssEntryGroup {
FONT,
GRAPHIC,
DIALOGUE,
+ EXTRADATA,
GROUP_MAX
};
diff --git a/src/ass_file.cpp b/src/ass_file.cpp
index 8ceafe5d1..9d07b6220 100644
--- a/src/ass_file.cpp
+++ b/src/ass_file.cpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
AssFile::AssFile() { }
@@ -54,6 +55,8 @@ void AssFile::LoadDefault(bool include_dialogue_line) {
AssFile::AssFile(const AssFile &from)
: Info(from.Info)
, Attachments(from.Attachments)
+, Extradata(from.Extradata)
+, next_extradata_id(from.next_extradata_id)
{
Styles.clone_from(from.Styles,
[](AssStyle const& e) { return new AssStyle(e); },
@@ -68,6 +71,8 @@ void AssFile::swap(AssFile& from) throw() {
Styles.swap(from.Styles);
Events.swap(from.Events);
Attachments.swap(from.Attachments);
+ Extradata.swap(from.Extradata);
+ std::swap(next_extradata_id, from.next_extradata_id);
}
AssFile& AssFile::operator=(AssFile from) {
@@ -229,3 +234,58 @@ void AssFile::Sort(EntryList &lst, CompFunc comp, std::set AssFile::GetExtradata(std::vector const& id_list) const {
+ // If multiple IDs have the same key name, the last ID wins
+ std::map result;
+ for (auto id : id_list) {
+ auto it = Extradata.find(id);
+ if (it != Extradata.end())
+ result[it->second.first] = it->second.second;
+ }
+ return result;
+}
+
+void AssFile::CleanExtradata() {
+ // Collect all IDs existing in the database
+ // Then remove all IDs found to be in use from this list
+ // Remaining is then all garbage IDs
+ std::vector ids;
+ for (auto& it : Extradata) {
+ ids.push_back(it.first);
+ }
+
+ // For each line, find which IDs it actually uses and remove them from the unused-list
+ for (auto& line : Events) {
+ // Find the ID for each unique key in the line
+ std::map key_ids;
+ for (auto id : line.ExtradataIds.get()) {
+ auto ed_it = Extradata.find(id);
+ if (ed_it == Extradata.end())
+ continue;
+ key_ids[ed_it->second.first] = id;
+ }
+ // Update the line's ID list to only contain the actual ID for any duplicate keys
+ // Also mark found IDs as used in the cleaning list
+ std::vector new_ids;
+ for (auto& keyid : key_ids) {
+ new_ids.push_back(keyid.second);
+ ids.erase(std::remove(ids.begin(), ids.end(), keyid.second));
+ }
+ line.ExtradataIds = new_ids;
+ }
+
+ // The ids list should contain only unused IDs now
+ for (auto id : ids) {
+ Extradata.erase(id);
+ }
+}
+
diff --git a/src/ass_file.h b/src/ass_file.h
index 7ed814e5e..9c1bd9ae8 100644
--- a/src/ass_file.h
+++ b/src/ass_file.h
@@ -50,6 +50,8 @@ class wxString;
template
using EntryList = typename boost::intrusive::make_list, boost::intrusive::base_hook>::type;
+using AegisubExtradataMap = std::map>;
+
struct AssFileCommit {
wxString const& message;
int *commit_id;
@@ -66,6 +68,9 @@ public:
EntryList Styles;
EntryList Events;
std::vector Attachments;
+ AegisubExtradataMap Extradata;
+
+ uint32_t next_extradata_id = 0;
AssFile();
AssFile(const AssFile &from);
@@ -102,6 +107,16 @@ public:
int GetUIStateAsInt(std::string const& key) const;
void SaveUIState(std::string const& key, std::string const& value);
+ /// @brief Add a new extradata entry
+ /// @param key Class identifier/owner for the extradata
+ /// @param value Data for the extradata
+ /// @return ID of the created entry
+ uint32_t AddExtradata(std::string const& key, std::string const& value);
+ /// Fetch all extradata entries from a list of IDs
+ std::map GetExtradata(std::vector const& id_list) const;
+ /// Remove unreferenced extradata entries
+ void CleanExtradata();
+
/// Type of changes made in a commit
enum CommitType {
/// Potentially the entire file has been changed; any saved information
@@ -129,7 +144,9 @@ public:
COMMIT_DIAG_TIME = 0x40,
/// The text of existing dialogue lines have changed
COMMIT_DIAG_TEXT = 0x80,
- COMMIT_DIAG_FULL = COMMIT_DIAG_META | COMMIT_DIAG_TIME | COMMIT_DIAG_TEXT
+ COMMIT_DIAG_FULL = COMMIT_DIAG_META | COMMIT_DIAG_TIME | COMMIT_DIAG_TEXT,
+ /// Extradata entries were added/modified/removed
+ COMMIT_EXTRADATA = 0x100,
};
DEFINE_SIGNAL_ADDERS(AnnounceCommit, AddCommitListener)
@@ -168,3 +185,4 @@ public:
/// @param limit If non-empty, only lines in this set are sorted
static void Sort(EntryList& lst, CompFunc comp = CompStart, std::set const& limit = std::set());
};
+
diff --git a/src/ass_parser.cpp b/src/ass_parser.cpp
index 7b193cce4..ced410ec4 100644
--- a/src/ass_parser.cpp
+++ b/src/ass_parser.cpp
@@ -19,14 +19,18 @@
#include "ass_file.h"
#include "ass_info.h"
#include "ass_style.h"
+#include "string_codec.h"
#include "subtitle_format.h"
+#include
#include
#include
#include
#include
#include
+#include
+#include
AssParser::AssParser(AssFile *target, int version)
: target(target)
@@ -113,6 +117,33 @@ void AssParser::ParseGraphicsLine(std::string const& data) {
attach = agi::make_unique(data, AssEntryGroup::GRAPHIC);
}
+void AssParser::ParseExtradataLine(std::string const &data) {
+ static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
+ boost::match_results mr;
+
+ if (boost::regex_match(data, mr, matcher)) {
+ auto id = boost::lexical_cast(mr.str(1));
+ auto key = inline_string_decode(mr.str(2));
+ auto valuetype = mr.str(3);
+ auto value = mr.str(4);
+ if (valuetype == "e") {
+ // escaped/inline_string encoded
+ value = inline_string_decode(value);
+ } else if (valuetype == "u") {
+ // ass uuencoded
+ auto valuedata = agi::ass::UUDecode(value);
+ value = std::string(valuedata.begin(), valuedata.end());
+ } else {
+ // unknown, error?
+ value = "";
+ }
+
+ // ensure next_extradata_id is always at least 1 more than the largest existing id
+ target->next_extradata_id = std::max(id+1, target->next_extradata_id);
+ target->Extradata[id] = std::make_pair(key, value);
+ }
+}
+
void AssParser::AddLine(std::string const& data) {
// Special-case for attachments since a line could theoretically be both a
// valid attachment data line and a valid section header, and if an
@@ -144,6 +175,8 @@ void AssParser::AddLine(std::string const& data) {
state = &AssParser::ParseGraphicsLine;
else if (low == "[fonts]")
state = &AssParser::ParseFontLine;
+ else if (low == "[aegisub extradata]")
+ state = &AssParser::ParseExtradataLine;
else
state = &AssParser::UnknownLine;
return;
diff --git a/src/ass_parser.h b/src/ass_parser.h
index b97bba766..c51c20204 100644
--- a/src/ass_parser.h
+++ b/src/ass_parser.h
@@ -31,6 +31,7 @@ class AssParser {
void ParseScriptInfoLine(std::string const& data);
void ParseFontLine(std::string const& data);
void ParseGraphicsLine(std::string const& data);
+ void ParseExtradataLine(std::string const &data);
void UnknownLine(std::string const&) { }
public:
AssParser(AssFile *target, int version);
diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp
index 23a112d21..c929def77 100644
--- a/src/auto4_lua.cpp
+++ b/src/auto4_lua.cpp
@@ -56,6 +56,7 @@
#include
#include
+#include
#include
#include
#include
@@ -274,6 +275,17 @@ namespace {
luaL_argcheck(L, lua_istable(L, 1), 1, "");
luaL_argcheck(L, lua_isstring(L, 2), 2, "");
+ // have to check that it looks like a style table before actually converting
+ // if it's a dialogue table then an active AssFile object is required
+ {
+ lua_getfield(L, 1, "class");
+ std::string actual_class{lua_tostring(L, -1)};
+ boost::to_lower(actual_class);
+ if (actual_class != "style")
+ return luaL_error(L, "Not a style entry");
+ lua_pop(L, 1);
+ }
+
lua_pushvalue(L, 1);
std::unique_ptr et(Automation4::LuaAssFile::LuaToAssEntry(L));
auto st = dynamic_cast(et.get());
@@ -295,6 +307,9 @@ namespace {
int luaopen_lpeg (lua_State *L);
+// Forward-declaration for luabins library (not in any public header)
+extern "C" int luaopen_luabins(lua_State * L);
+
namespace Automation4 {
int regex_init(lua_State *L);
@@ -365,6 +380,7 @@ namespace Automation4 {
push_value(L, luaopen_package); lua_call(L, 0, 0);
push_value(L, luaopen_string); lua_call(L, 0, 0);
push_value(L, luaopen_table); lua_call(L, 0, 0);
+ push_value(L, luaopen_luabins); lua_call(L, 0, 0);
_stackcheck.check_stack(0);
// dofile and loadfile are replaced with include
diff --git a/src/auto4_lua.h b/src/auto4_lua.h
index 74da7af44..ef06f694f 100644
--- a/src/auto4_lua.h
+++ b/src/auto4_lua.h
@@ -124,7 +124,7 @@ namespace Automation4 {
/// makes a Lua representation of AssEntry and places on the top of the stack
void AssEntryToLua(lua_State *L, size_t idx);
/// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
- static std::unique_ptr LuaToAssEntry(lua_State *L);
+ static std::unique_ptr LuaToAssEntry(lua_State *L, AssFile *ass=0);
/// @brief Signal that the script using this file is now done running
/// @param set_undo If there's any uncommitted changes to the file,
diff --git a/src/auto4_lua_assfile.cpp b/src/auto4_lua_assfile.cpp
index 774b2e10d..74024071d 100644
--- a/src/auto4_lua_assfile.cpp
+++ b/src/auto4_lua_assfile.cpp
@@ -173,6 +173,15 @@ namespace Automation4 {
set_field(L, "text", dia->Text);
+ // create extradata table
+ lua_newtable(L);
+ for (auto const& ed : ass->GetExtradata(dia->ExtradataIds)) {
+ push_value(L, ed.first);
+ push_value(L, ed.second);
+ lua_settable(L, -3);
+ }
+ lua_setfield(L, -2, "extra");
+
set_field(L, "class", "dialogue");
}
else if (auto sty = dynamic_cast(e)) {
@@ -221,7 +230,7 @@ namespace Automation4 {
}
}
- std::unique_ptr LuaAssFile::LuaToAssEntry(lua_State *L)
+ std::unique_ptr LuaAssFile::LuaToAssEntry(lua_State *L, AssFile *ass)
{
// assume an assentry table is on the top of the stack
// convert it to a real AssEntry object, and pop the table from the stack
@@ -271,6 +280,7 @@ namespace Automation4 {
sty->UpdateData();
}
else if (lclass == "dialogue") {
+ assert(ass != 0); // since we need AssFile::AddExtradata
auto dia = new AssDialogue;
result.reset(dia);
@@ -285,6 +295,18 @@ namespace Automation4 {
dia->Margin[2] = get_int_field(L, "margin_t", "dialogue");
dia->Effect = get_string_field(L, "effect", "dialogue");
dia->Text = get_string_field(L, "text", "dialogue");
+
+ lua_getfield(L, -1, "extra");
+ std::vector new_ids;
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ auto key = get_string_or_default(L, -2);
+ auto value = get_string_or_default(L, -1);
+ new_ids.push_back(ass->AddExtradata(key, value));
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ dia->ExtradataIds = new_ids;
}
else {
luaL_error(L, "Found line with unknown class: %s", lclass.c_str());
@@ -422,7 +444,7 @@ namespace Automation4 {
// insert
CheckBounds(n);
- auto e = LuaToAssEntry(L);
+ auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
QueueLineForDeletion(n - 1);
AssignLine(n - 1, std::move(e));
@@ -511,7 +533,7 @@ namespace Automation4 {
for (int i = 1; i <= n; i++) {
lua_pushvalue(L, i);
- auto e = LuaToAssEntry(L);
+ auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
if (lines.empty()) {
@@ -555,7 +577,7 @@ namespace Automation4 {
new_entries.reserve(n - 1);
for (int i = 2; i <= n; i++) {
lua_pushvalue(L, i);
- auto e = LuaToAssEntry(L);
+ auto e = LuaToAssEntry(L, ass);
modification_type |= modification_mask(e.get());
InsertLine(new_entries, i - 2, std::move(e));
lua_pop(L, 1);
@@ -594,7 +616,7 @@ namespace Automation4 {
int LuaAssFile::LuaParseKaraokeData(lua_State *L)
{
- auto e = LuaToAssEntry(L);
+ auto e = LuaToAssEntry(L, ass);
auto dia = dynamic_cast(e.get());
luaL_argcheck(L, dia, 1, "Subtitle line must be a dialogue line");
diff --git a/src/auto4_lua_utils.h b/src/auto4_lua_utils.h
index a9e9c9a92..594eec419 100644
--- a/src/auto4_lua_utils.h
+++ b/src/auto4_lua_utils.h
@@ -40,11 +40,12 @@ inline void push_value(lua_State *L, wxString const& value) {
}
inline void push_value(lua_State *L, agi::fs::path const& value) {
- lua_pushstring(L, value.string().c_str());
+ std::string strval = value.string();
+ lua_pushlstring(L, strval.c_str(), strval.size());
}
inline void push_value(lua_State *L, std::string const& value) {
- lua_pushstring(L, value.c_str());
+ lua_pushlstring(L, value.c_str(), value.size());
}
inline void push_value(lua_State *L, lua_CFunction value) {
@@ -71,10 +72,11 @@ inline wxString check_wxstring(lua_State *L, int idx) {
}
inline std::string get_string_or_default(lua_State *L, int idx) {
- const char *str = lua_tostring(L, idx);
+ size_t len = 0;
+ const char *str = lua_tolstring(L, idx, &len);
if (!str)
str = "";
- return str;
+ return std::string(str, len);
}
inline std::string get_global_string(lua_State *L, const char *name) {
diff --git a/src/string_codec.cpp b/src/string_codec.cpp
index 5e48d214a..6e19c7fbb 100644
--- a/src/string_codec.cpp
+++ b/src/string_codec.cpp
@@ -45,7 +45,7 @@ std::string inline_string_encode(const std::string &input) {
auto format = boost::format("#%02X");
for (char c : input) {
if (c <= 0x1F || c == 0x23 || c == 0x2C || c == 0x3A || c == 0x7C)
- output += str(format % c);
+ output += str(format % (int)(unsigned char)c);
else
output += c;
}
diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp
index 4f3aae793..de1f8a954 100644
--- a/src/subs_controller.cpp
+++ b/src/subs_controller.cpp
@@ -60,6 +60,7 @@ struct SubsController::UndoInfo {
std::vector styles;
std::vector events;
std::vector attachments;
+ AegisubExtradataMap extradata;
mutable std::vector selection;
int active_line_id = 0;
@@ -69,6 +70,7 @@ struct SubsController::UndoInfo {
: undo_description(d)
, commit_id(commit_id)
, attachments(c->ass->Attachments)
+ , extradata(c->ass->Extradata)
{
script_info.reserve(c->ass->Info.size());
for (auto const& info : c->ass->Info)
@@ -108,6 +110,7 @@ struct SubsController::UndoInfo {
if (binary_search(begin(selection), end(selection), copy->Id))
new_sel.insert(copy);
}
+ c->ass->Extradata = extradata;
c->ass->Commit("", AssFile::COMMIT_NEW);
c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
@@ -267,6 +270,8 @@ void SubsController::Save(agi::fs::path const& filename, std::string const& enco
FileSave();
+ context->ass->CleanExtradata();
+
writer->WriteFile(context->ass.get(), filename, 0, encoding);
}
catch (...) {
diff --git a/src/subtitle_format_ass.cpp b/src/subtitle_format_ass.cpp
index 3fdc30e39..c5d1800df 100644
--- a/src/subtitle_format_ass.cpp
+++ b/src/subtitle_format_ass.cpp
@@ -22,10 +22,12 @@
#include "ass_file.h"
#include "ass_style.h"
#include "ass_parser.h"
+#include "string_codec.h"
#include "text_file_reader.h"
#include "text_file_writer.h"
#include "version.h"
+#include
#include
DEFINE_SIMPLE_EXCEPTION(AssParseError, SubtitleFormatParseError, "subtitle_io/parse/ass")
@@ -114,6 +116,35 @@ struct Writer {
file.WriteLineToFile(ssa ? line.GetSSAText() : line.GetEntryData());
}
}
+
+ void WriteExtradata(AegisubExtradataMap const& extradata) {
+ if (extradata.size() == 0)
+ return;
+
+ group = AssEntryGroup::EXTRADATA;
+ file.WriteLineToFile("");
+ file.WriteLineToFile("[Aegisub Extradata]");
+ for (auto const& edi : extradata) {
+ std::string line = "Data: ";
+ line += std::to_string(edi.first);
+ line += ",";
+ line += inline_string_encode(edi.second.first);
+ line += ",";
+ std::string encoded_data = inline_string_encode(edi.second.second);
+ if (4*edi.second.second.size() < 3*encoded_data.size()) {
+ // the inline_string encoding grew the data by more than uuencoding would
+ // so base64 encode it instead
+ line += "u"; // marker for uuencoding
+ encoded_data = agi::ass::UUEncode(edi.second.second, false);
+ printf("did uuencoding, original size=%lu, encoded size=%lu\n", edi.second.second.size(), encoded_data.size());
+ line += encoded_data;
+ } else {
+ line += "e"; // marker for inline_string encoding (escaping)
+ line += encoded_data;
+ }
+ file.WriteLineToFile(line);
+ }
+ }
};
}
@@ -124,4 +155,5 @@ void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen
writer.Write(src->Styles);
writer.Write(src->Attachments);
writer.Write(src->Events);
+ writer.WriteExtradata(src->Extradata);
}
diff --git a/vendor/luabins/AUTHORS b/vendor/luabins/AUTHORS
new file mode 100644
index 000000000..c5bde58ea
--- /dev/null
+++ b/vendor/luabins/AUTHORS
@@ -0,0 +1,4 @@
+Luabins authors:
+----------------
+
+Alexander Gladysh
diff --git a/vendor/luabins/BENCHMARK b/vendor/luabins/BENCHMARK
new file mode 100644
index 000000000..b74356c44
--- /dev/null
+++ b/vendor/luabins/BENCHMARK
@@ -0,0 +1,51 @@
+Luabins 0.2 benchmark (see etc/benchmark.lua) results on
+
+ MacBook Pro 2.4 GHz Intel Core Duo 2.66 MB DDR2 SDRAM
+ OS X 10.6.2
+ GCC 4.2.1 (Apple Inc. build 5646) (dot 1)
+ Lua 5.1.4 from MacPorts
+ Luabins built with default Makefile configuration
+
+Note that the data used in benchmark is quite trivial. You're advised
+to find out if Luabins is "fast enough" for you by yourself.
+
+Lua
+-------------------------------------------------------------------
+ name | rel | abs s / iter = us (1e-6 s) / iter
+-------------------------------------------------------------------
+ luabins_save | 1.0000 | 4.21 / 1000000 = 4.210000 us
+ luabins_load | 1.2043 | 5.07 / 1000000 = 5.070000 us
+ loadstring | 4.7435 | 19.97 / 1000000 = 19.970000 us
+ concat | 10.6413 | 44.80 / 1000000 = 44.800000 us
+
+===================================================================
+
+Luabins 0.1 benchmark (see etc/benchmark.lua) results on
+
+ MacBook Pro 2.4 GHz Intel Core Duo 2.66 MB DDR2 SDRAM
+ OS X 10.5.6
+ GCC 4.0.1 (Apple Inc. build 5490)
+ Lua 5.1.4 from MacPorts
+ LuaJIT 1.1.3 built from sources with default configuration
+ Luabins built with default configuration
+
+Note that the data used in benchmark is quite trivial. You're advised
+to find out if Luabins is "fast enough" for you by yourself.
+
+Lua
+-------------------------------------------------------------------
+ name | rel | abs s / iter = us (1e-6 s) / iter
+-------------------------------------------------------------------
+ luabins_load | 1.0000 | 6.34 / 1000000 = 6.340000 us
+ luabins_save | 1.7256 | 10.94 / 1000000 = 10.940000 us
+ loadstring | 3.6530 | 23.16 / 1000000 = 23.160000 us
+ concat | 10.0741 | 63.87 / 1000000 = 63.870000 us
+
+LuaJIT -O
+-------------------------------------------------------------------
+ name | rel | abs s / iter = us (1e-6 s) / iter
+-------------------------------------------------------------------
+ luabins_load | 1.0000 | 5.40 / 1000000 = 5.400000 us
+ luabins_save | 1.6111 | 8.70 / 1000000 = 8.700000 us
+ concat | 6.6630 | 35.98 / 1000000 = 35.980000 us
+ loadstring | 23.6370 | 127.64 / 1000000 = 127.640000 us
diff --git a/vendor/luabins/COPYRIGHT b/vendor/luabins/COPYRIGHT
new file mode 100644
index 000000000..da6420e4b
--- /dev/null
+++ b/vendor/luabins/COPYRIGHT
@@ -0,0 +1,32 @@
+Luabins License
+---------------
+
+Luabins is licensed under the terms of the MIT license reproduced below.
+This means that luabins is free software and can be used for both academic
+and commercial purposes at absolutely no cost.
+
+===============================================================================
+
+Copyright (C) 2009-2010 Luabins authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+
+(end of COPYRIGHT)
diff --git a/vendor/luabins/HISTORY b/vendor/luabins/HISTORY
new file mode 100644
index 000000000..f235214ea
--- /dev/null
+++ b/vendor/luabins/HISTORY
@@ -0,0 +1,47 @@
+v0.3
+====
+
+Format unification for x86 and x86_64. Bug fixes.
+
+WARNING: Format is not compatible with 0.2 and below.
+ Only data saved on x86_64 is affected, though.
+ Data, saved on x86, should load fine.
+
+New features:
+
+-- Format change: unified save format for x86 vs. x86_64.
+-- API to save data to FILE * stream without Lua (see fwrite.h).
+
+Bug fixes:
+
+-- Load: fixed bug in readbyte, now it checks if we have data before read.
+-- Load: fixed Lua C stack overflow bug on large data.
+
+Misc:
+
+-- Better module information.
+ Replaced luabins.VERSION with _VERSION, _DESCRIPTION and _COPYRIGHT.
+-- Added some CLI tools, useful for Luabins development (see etc/).
+-- Some code cleanup.
+
+v0.2
+====
+
+Lua-less saving.
+
+-- New, 2x faster luabins.save() (see BENCHMARK).
+-- API to save data manually, without Lua (see write.h).
+-- Added Luarocks rockspecs (see rockspec/).
+-- Fixed Makefile for Ubuntu.
+
+v0.1.1
+======
+
+Bugfix release.
+
+-- Fixed handling of array holes in Lua tables.
+
+v0.1
+====
+
+Initial release.
diff --git a/vendor/luabins/Makefile b/vendor/luabins/Makefile
new file mode 100644
index 000000000..6713d3759
--- /dev/null
+++ b/vendor/luabins/Makefile
@@ -0,0 +1,40 @@
+include ../../Makefile.inc
+
+LIB = libluabins.a
+
+CXXFLAGS += -I../lua/src -DLUABINS_LUABUILTASCPP
+
+SRC = \
+ src/fwrite.cpp \
+ src/load.cpp \
+ src/luabins.cpp \
+ src/luainternals.cpp \
+ src/save.cpp \
+ src/savebuffer.cpp \
+ src/write.cpp
+
+HEADER = \
+ src/fwrite.h \
+ src/luabins.h \
+ src/luaheaders.h \
+ src/luainternals.h \
+ src/savebuffer.h \
+ src/saveload.h \
+ src/write.h
+
+EXTRA_DIST = \
+ src/lualess.c \
+ src/lualess.h \
+ etc/benchmark.lua \
+ etc/checkfmt.lua \
+ etc/dataset.lua \
+ etc/tolua.lua \
+ etc/toluabins.lua \
+ AUTHORS \
+ COPYRIGHT \
+ HISTORY \
+ README.md \
+ TODO
+
+include ../../Makefile.target
+-include src/*.d
diff --git a/vendor/luabins/README.md b/vendor/luabins/README.md
new file mode 100644
index 000000000..02b3b17b0
--- /dev/null
+++ b/vendor/luabins/README.md
@@ -0,0 +1,112 @@
+luabins — Lua Binary Serialization Library
+==========================================
+
+Allows to save tuples of primitive Lua types into binary chunks
+and to load saved data back.
+
+On serialization
+----------------
+
+### Luabins works with
+
+ * `nil`
+ * `boolean`
+ * `number`
+ * `string`
+ * `table` (see below)
+
+### Luabins refuses to save
+
+ * `function`
+ * `thread`
+ * `userdata`
+
+Luabins intentionally does not save or check any meta-information
+(versions, endianness etc.) along with data. If needed, it is to be handled
+elsewhere.
+
+### Table serialization
+
+1. Metatatables are ignored.
+2. Table nesting depth should be no more than `LUABINS_MAXTABLENESTING`.
+3. On table save references are not honored. Each encountered reference
+ becomes independent object on load:
+
+ local t = { 42 }
+ { t, t }
+
+ becomes
+
+ { { 42 }, { 42 } }
+
+ that is, three separate tables instead of two.
+
+Lua API
+-------
+
+ * `luabins.save(...)`
+
+ Saves arguments into a binary string.
+
+ * On success returns that string.
+ * On failure returns nil and error message.
+
+ Example:
+
+ local str = assert(luabins.save(1, "two", { "three", 4 }))
+
+ * `luabins.load(string)`
+
+ Loads a list of values from a binary string.
+
+ * On success returns true and loaded values.
+ * On failure returns nil and error message.
+
+ Example:
+
+ If you do not know in advance what data is stored inside a binary string,
+ you may put results into a table:
+
+ local values = { luabins.load(data) }
+ assert(values[1], values[2])
+
+ If you know how to handle stored values (for example you're sure they were
+ generated following some established protocol), you may want to use
+ something like this function to check `luabins.load()` result:
+
+ function eat_true(t, ...)
+ assert(t, ...)
+ return ...
+ end
+
+ my_value_handler(eat_true(luabins.load(data)))
+
+C API
+-----
+
+ * `int luabins_save(lua_State * L, int index_from, int index_to)`
+
+ Save Lua values from given state at given stack index range.
+ Lua value is left untouched. Note that empty range is not an error.
+ You may save from 0 to `LUABINS_MAXTUPLE` values.
+ Note only real non-negative indices work.
+
+ * On success returns 0, pushes saved data as a string on the top of stack.
+ * On failure returns non-zero, pushes error message on the top
+ of the stack.
+
+ * `int luabins_load(lua_State * L, const unsigned char * data,
+ size_t len, int *count)`
+
+ Load Lua values from given byte chunk.
+
+ * On success returns 0, pushes loaded values on stack.
+ Sets count to the number of values pushed.
+ Note that to have zero loaded items is a valid scenario.
+ * On failure returns non-zero, pushes error message on the top
+ of the stack.
+
+Luabins is still an experimental volatile software.
+Please see source code for more documentation.
+
+See the copyright information in the file named `COPYRIGHT`.
diff --git a/vendor/luabins/TODO b/vendor/luabins/TODO
new file mode 100644
index 000000000..71c317f13
--- /dev/null
+++ b/vendor/luabins/TODO
@@ -0,0 +1,11 @@
+-- Add key-value size-less table format (see luatexts)
+-- Add utf-8 char-length string format (see luatexts)
+-- Add autotest targets for LuaJIT and LuaJIT 2 x86, x86_64, both for Lua and C.
+-- Test on Lua with custom allocator.
+-- Use Makefile in rockspec?
+-- Enhance "corrupt data" message on load. Need more info on what is wrong.
+ Ensure every case is covered with tests.
+-- Autocompact integers (especially strings -- most of them do not need size_t!)
+-- Better cover new exponential growth strategy corner-cases with tests.
+-- Shouldn't write and fwrite have common codebase?
+-- Document write.h and fwrite.h
diff --git a/vendor/luabins/etc/benchmark.lua b/vendor/luabins/etc/benchmark.lua
new file mode 100644
index 000000000..7ed8c4d58
--- /dev/null
+++ b/vendor/luabins/etc/benchmark.lua
@@ -0,0 +1,106 @@
+-- This benchmark is compatible with luamarca benchmarking system
+-- http://github.com/agladysh/luamarca
+
+package.cpath = "./?.so;"..package.cpath
+
+local luabins = require("luabins")
+
+local table_concat = table.concat
+local loadstring, assert = loadstring, assert
+local pairs, type, tostring = pairs, type, tostring
+local luabins_save, luabins_load = luabins.save, luabins.load
+
+local lua = ([[return {
+ true, false, 42, "string",
+ [{
+ true, false, 42, "string",
+ [true] = true, [false] = false, [42] = 42, string = "string"
+ }] =
+ {
+ true, false, 42, "string",
+ [true] = true, [false] = false, [42] = 42, string = "string"
+ }
+}]]):gsub("[%s\n]+", "") -- Remove spaces for compactness
+
+local data = assert(loadstring(lua))()
+local saved = assert(luabins_save(data))
+
+-- Imagine we know exact data structure.
+-- We still impose some overhead on table.concat() related
+-- stuff, since it is more realistic scenario.
+-- Still looks a bit silly.
+local concat = function(data)
+ local buf = {}
+ local function cat(v) buf[#buf + 1] = tostring(v) return cat end
+
+ -- Find table key
+ local tablekey, tableval
+ for k, v in pairs(data) do
+ if type(k) == "table" then
+ tablekey, tableval = k, v
+ break
+ end
+ end
+
+ cat 'return{'
+
+ cat (data[1]) ','
+ cat (data[2]) ','
+ cat (data[3]) ','
+ cat '"' (data[4]) '",'
+
+ cat '[{'
+
+ cat (tablekey[1]) ','
+ cat (tablekey[2]) ','
+ cat (tablekey[3]) ','
+ cat '"' (tablekey[4]) '",'
+
+ cat '[' (true) ']=' (tablekey[true]) ','
+ cat '[' (false) ']=' (tablekey[false]) ','
+ cat '[' (42) ']=' (tablekey[42]) ','
+ cat 'string' '=' '"' (tablekey["string"]) '"'
+
+ cat '}]='
+
+ cat '{'
+
+ cat (tablekey[1]) ','
+ cat (tablekey[2]) ','
+ cat (tablekey[3]) ','
+ cat '"' (tablekey[4]) '",'
+
+ cat '[' (true) ']=' (tablekey[true]) ','
+ cat '[' (false) ']=' (tablekey[false]) ','
+ cat '[' (42) ']=' (tablekey[42]) ','
+ cat 'string' '=' '"' (tablekey["string"]) '"'
+
+ cat '}'
+
+ cat '}'
+
+ return table_concat(buf, '')
+end
+
+-- Sanity check
+assert(concat(data) == lua)
+
+local bench = {}
+
+bench.concat = function()
+ assert(concat(data))
+end
+
+bench.loadstring = function()
+ assert(loadstring(lua))()
+end
+
+bench.luabins_save = function()
+ assert(luabins_save(data))
+end
+
+bench.luabins_load = function()
+ assert(luabins_load(saved))
+end
+
+return bench
diff --git a/vendor/luabins/etc/checkfmt.lua b/vendor/luabins/etc/checkfmt.lua
new file mode 100644
index 000000000..c1bf35320
--- /dev/null
+++ b/vendor/luabins/etc/checkfmt.lua
@@ -0,0 +1,30 @@
+--- Luabins format checker
+
+package.cpath = "./lib/?.so;"..package.cpath
+
+local luabins = require("luabins")
+
+local luabins_save, luabins_load = luabins.save, luabins.load
+
+local filename = select(1, ...)
+assert(filename, "Usage: lua checkfmt.lua ")
+
+local file
+if filename == "-" then
+ file = io.stdin
+else
+ file = assert(io.open(filename, "r"))
+end
+
+assert(
+ luabins_load(
+ file:read("*a")
+ )
+ )
+
+if file ~= io.stdin then
+ file:close()
+end
+file = nil
+
+print("OK")
diff --git a/vendor/luabins/etc/dataset.lua b/vendor/luabins/etc/dataset.lua
new file mode 100644
index 000000000..f6bceacae
--- /dev/null
+++ b/vendor/luabins/etc/dataset.lua
@@ -0,0 +1,90 @@
+-- Random Luabins dataset generator
+
+package.cpath = "./lib/?.so;"..package.cpath
+
+local luabins = require("luabins")
+
+local luabins_save, luabins_load = luabins.save, luabins.load
+
+math.randomseed(123456)
+
+-- TODO: Generalize. Copy-paste from test.lua
+local function gen_random_dataset(num, nesting)
+ num = num or math.random(0, 128)
+ nesting = nesting or 1
+
+ local gen_str = function()
+ local t = {}
+ local n = math.random(0, 1024)
+ for i = 1, n do
+ t[i] = string.char(math.random(0, 255))
+ end
+ return table.concat(t)
+ end
+
+ local gen_bool = function() return math.random() >= 0.5 end
+
+ local gen_nil = function() return nil end
+
+ local generators =
+ {
+ gen_nil;
+ gen_nil;
+ gen_nil;
+ gen_bool;
+ gen_bool;
+ gen_bool;
+ function() return math.random() end;
+ function() return math.random(-10000, 10000) end;
+ function() return math.random() * math.random(-10000, 10000) end;
+ gen_str;
+ gen_str;
+ gen_str;
+ function()
+ if nesting >= 24 then
+ return nil
+ end
+
+ local t = {}
+ local n = math.random(0, 24 - nesting)
+ for i = 1, n do
+ local k = gen_random_dataset(1, nesting + 1)
+ if k == nil then
+ k = "(nil)"
+ end
+ t[ k ] = gen_random_dataset(
+ 1,
+ nesting + 1
+ )
+ end
+
+ return t
+ end;
+ }
+
+ local t = {}
+ for i = 1, num do
+ local n = math.random(1, #generators)
+ t[i] = generators[n]()
+ end
+ return unpack(t, 0, num)
+end
+
+local saved = assert(luabins_save(gen_random_dataset()))
+
+local filename = select(1, ...)
+assert(filename, "Usage: lua dataset.lua ")
+
+local file
+if filename == "-" then
+ file = io.stdout
+else
+ file = assert(io.open(filename, "w"))
+end
+
+file:write(saved)
+
+if file ~= io.stdout then
+ file:close()
+end
+file = nil
diff --git a/vendor/luabins/etc/tolua.lua b/vendor/luabins/etc/tolua.lua
new file mode 100644
index 000000000..f70990547
--- /dev/null
+++ b/vendor/luabins/etc/tolua.lua
@@ -0,0 +1,39 @@
+-- Luabins to Lua converter
+-- Requires lua-nucleo (http://github.com/lua-nucleo/lua-nucleo/)
+
+package.cpath = "./lib/?.so;"..package.cpath
+
+local luabins = require 'luabins'
+
+dofile('lua-nucleo/strict.lua')
+dofile('lua-nucleo/import.lua')
+
+local tserialize = import 'lua-nucleo/tserialize.lua' { 'tserialize' }
+
+local filename = select(1, ...)
+assert(filename, "Usage: lua tolua.lua ")
+
+local file
+if filename == "-" then
+ file = io.stdin
+else
+ file = assert(io.open(filename, "r"))
+end
+
+io.write(
+ tserialize(
+ assert(
+ luabins.load(
+ file:read("*a")
+ )
+ )
+ )
+ )
+
+io:flush()
+
+if file ~= io.stdin then
+ file:close()
+end
+file = nil
+
diff --git a/vendor/luabins/etc/toluabins.lua b/vendor/luabins/etc/toluabins.lua
new file mode 100644
index 000000000..9880c73b7
--- /dev/null
+++ b/vendor/luabins/etc/toluabins.lua
@@ -0,0 +1,16 @@
+-- Lua to Luabins converter
+
+package.cpath = "./lib/?.so;"..package.cpath
+
+local luabins = require 'luabins'
+
+local filename = select(1, ...)
+assert(filename, "Usage: lua toluabins.lua ")
+
+io.write(
+ luabins.save(
+ assert(loadfile(filename))()
+ )
+ )
+
+io:flush()
diff --git a/vendor/luabins/include/.keepme b/vendor/luabins/include/.keepme
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/luabins/lib/.keepme b/vendor/luabins/lib/.keepme
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/luabins/obj/.keepme b/vendor/luabins/obj/.keepme
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/luabins/rockspec/luabins-0.1.1-1.rockspec b/vendor/luabins/rockspec/luabins-0.1.1-1.rockspec
new file mode 100644
index 000000000..2fce4970f
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-0.1.1-1.rockspec
@@ -0,0 +1,19 @@
+package = "luabins"
+version = "0.1.1-1"
+source = {
+ url = "http://cloud.github.com/downloads/agladysh/luabins/luabins-0.1.1.tar.gz"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "make"
+}
diff --git a/vendor/luabins/rockspec/luabins-0.1.1-2.rockspec b/vendor/luabins/rockspec/luabins-0.1.1-2.rockspec
new file mode 100644
index 000000000..bacadd8a2
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-0.1.1-2.rockspec
@@ -0,0 +1,32 @@
+package = "luabins"
+version = "0.1.1-2"
+source = {
+ url = "http://cloud.github.com/downloads/agladysh/luabins/luabins-0.1.1.tar.gz"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ luabins = {
+ sources = {
+ "src/load.c",
+ "src/luabins.c",
+ "src/luainternals.c",
+ "src/save.c"
+ },
+ incdirs = {
+ "src/"
+ }
+ }
+ }
+}
diff --git a/vendor/luabins/rockspec/luabins-0.2-1.rockspec b/vendor/luabins/rockspec/luabins-0.2-1.rockspec
new file mode 100644
index 000000000..f91848e98
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-0.2-1.rockspec
@@ -0,0 +1,35 @@
+package = "luabins"
+version = "0.2-1"
+source = {
+ url = "http://cloud.github.com/downloads/agladysh/luabins/luabins-0.2.tar.gz"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ luabins = {
+ sources = {
+ "src/load.c",
+ "src/luabins.c",
+ "src/luainternals.c",
+ "src/lualess.c",
+ "src/save.c",
+ "src/savebuffer.c",
+ "src/write.c"
+ },
+ incdirs = {
+ "src/"
+ }
+ }
+ }
+}
diff --git a/vendor/luabins/rockspec/luabins-0.3-1.rockspec b/vendor/luabins/rockspec/luabins-0.3-1.rockspec
new file mode 100644
index 000000000..d97beef77
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-0.3-1.rockspec
@@ -0,0 +1,35 @@
+package = "luabins"
+version = "0.3-1"
+source = {
+ url = "http://cloud.github.com/downloads/agladysh/luabins/luabins-0.3.tar.gz"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ luabins = {
+ sources = {
+ "src/load.c",
+ "src/luabins.c",
+ "src/luainternals.c",
+ "src/lualess.c",
+ "src/save.c",
+ "src/savebuffer.c",
+ "src/write.c"
+ },
+ incdirs = {
+ "src/"
+ }
+ }
+ }
+}
diff --git a/vendor/luabins/rockspec/luabins-scm-1.rockspec b/vendor/luabins/rockspec/luabins-scm-1.rockspec
new file mode 100644
index 000000000..44d15f874
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-scm-1.rockspec
@@ -0,0 +1,32 @@
+package = "luabins"
+version = "scm-1"
+source = {
+ url = "git://github.com/agladysh/luabins.git"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ luabins = {
+ sources = {
+ "src/load.c",
+ "src/luabins.c",
+ "src/luainternals.c",
+ "src/save.c"
+ },
+ incdirs = {
+ "src/"
+ }
+ }
+ }
+}
diff --git a/vendor/luabins/rockspec/luabins-scm-2.rockspec b/vendor/luabins/rockspec/luabins-scm-2.rockspec
new file mode 100644
index 000000000..f1b5e3a30
--- /dev/null
+++ b/vendor/luabins/rockspec/luabins-scm-2.rockspec
@@ -0,0 +1,35 @@
+package = "luabins"
+version = "scm-2"
+source = {
+ url = "git://github.com/agladysh/luabins.git"
+}
+description = {
+ summary = "Trivial Lua Binary Serialization Library",
+ detailed = [[
+ Luabins allows to save tuples of primitive Lua types into binary chunks and to load saved data back.
+ ]],
+ homepage = "http://github.com/agladysh/luabins",
+ license = "MIT/X11"
+}
+dependencies = {
+ "lua >= 5.1"
+}
+build = {
+ type = "builtin",
+ modules = {
+ luabins = {
+ sources = {
+ "src/load.c",
+ "src/luabins.c",
+ "src/luainternals.c",
+ "src/lualess.c",
+ "src/save.c",
+ "src/savebuffer.c",
+ "src/write.c"
+ },
+ incdirs = {
+ "src/"
+ }
+ }
+ }
+}
diff --git a/vendor/luabins/src/fwrite.cpp b/vendor/luabins/src/fwrite.cpp
new file mode 100644
index 000000000..99591e7f2
--- /dev/null
+++ b/vendor/luabins/src/fwrite.cpp
@@ -0,0 +1,49 @@
+/*
+* fwrite.c
+* Luabins Lua-less write API using FILE * as buffer
+* See copyright notice in luabins.h
+*/
+
+#include "luaheaders.h"
+
+#include "fwrite.h"
+
+/* TODO: Note that stream errors are ignored. Handle them better? */
+
+void lbs_fwriteTableHeader(
+ FILE * f,
+ int array_size,
+ int hash_size
+ )
+{
+ fputc(LUABINS_CTABLE, f);
+ fwrite(
+ (const unsigned char *)&array_size,
+ LUABINS_LINT,
+ 1,
+ f
+ );
+ fwrite(
+ (const unsigned char *)&hash_size,
+ LUABINS_LINT,
+ 1,
+ f
+ );
+}
+
+void lbs_fwriteNumber(FILE * f, lua_Number value)
+{
+ fputc(LUABINS_CNUMBER, f);
+ fwrite((const unsigned char *)&value, LUABINS_LNUMBER, 1, f);
+}
+
+void lbs_fwriteString(
+ FILE * f,
+ const char * value,
+ size_t length
+ )
+{
+ fputc(LUABINS_CSTRING, f);
+ fwrite((const unsigned char *)&length, LUABINS_LSIZET, 1, f);
+ fwrite((const unsigned char *)value, length, 1, f);
+}
diff --git a/vendor/luabins/src/fwrite.h b/vendor/luabins/src/fwrite.h
new file mode 100644
index 000000000..ea27cbcd4
--- /dev/null
+++ b/vendor/luabins/src/fwrite.h
@@ -0,0 +1,37 @@
+/*
+* fwrite.h
+* Luabins Lua-less write API using FILE * as buffer
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_FWRITE_H_INCLUDED_
+#define LUABINS_FWRITE_H_INCLUDED_
+
+#include "saveload.h"
+
+#define lbs_fwriteTupleSize(f, tuple_size) \
+ fputc((int)(tuple_size), (f))
+
+void lbs_fwriteTableHeader(
+ FILE * f,
+ int array_size,
+ int hash_size
+ );
+
+#define lbs_fwriteNil(f) \
+ fputc(LUABINS_CNIL, (f))
+
+#define lbs_fwriteBoolean(f, value) \
+ fputc(((value) == 0) ? LUABINS_CFALSE : LUABINS_CTRUE, (f))
+
+void lbs_fwriteNumber(FILE * f, lua_Number value);
+
+#define lbs_fwriteInteger lbs_fwriteNumber
+
+void lbs_fwriteString(
+ FILE * f,
+ const char * value,
+ size_t length
+ );
+
+#endif /* LUABINS_FWRITE_H_INCLUDED_ */
diff --git a/vendor/luabins/src/load.cpp b/vendor/luabins/src/load.cpp
new file mode 100644
index 000000000..0c7dcba38
--- /dev/null
+++ b/vendor/luabins/src/load.cpp
@@ -0,0 +1,367 @@
+/*
+* load.c
+* Luabins load code
+* See copyright notice in luabins.h
+*/
+
+#include
+
+#include "luaheaders.h"
+
+#include "luabins.h"
+#include "saveload.h"
+#include "luainternals.h"
+
+#if 0
+ #define XSPAM(a) printf a
+#else
+ #define XSPAM(a) (void)0
+#endif
+
+#if 0
+ #define SPAM(a) printf a
+#else
+ #define SPAM(a) (void)0
+#endif
+
+typedef struct lbs_LoadState
+{
+ const unsigned char * pos;
+ size_t unread;
+} lbs_LoadState;
+
+static void lbsLS_init(
+ lbs_LoadState * ls,
+ const unsigned char * data,
+ size_t len
+ )
+{
+ ls->pos = (len > 0) ? data : NULL;
+ ls->unread = len;
+}
+
+#define lbsLS_good(ls) \
+ ((ls)->pos != NULL)
+
+#define lbsLS_unread(ls) \
+ ((ls)->unread)
+
+static unsigned char lbsLS_readbyte(lbs_LoadState * ls)
+{
+ if (lbsLS_good(ls))
+ {
+ if (lbsLS_unread(ls) > 0)
+ {
+ const unsigned char b = *ls->pos;
+ ++ls->pos;
+ --ls->unread;
+ return b;
+ }
+ else
+ {
+ ls->unread = 0;
+ ls->pos = NULL;
+ }
+ }
+ return 0;
+}
+
+static const unsigned char * lbsLS_eat(lbs_LoadState * ls, size_t len)
+{
+ const unsigned char * result = NULL;
+ if (lbsLS_good(ls))
+ {
+ if (lbsLS_unread(ls) >= len)
+ {
+ XSPAM(("* eat: len %u\n", (int)len));
+ result = ls->pos;
+ ls->pos += len;
+ ls->unread -= len;
+ XSPAM(("* eat: done len %u\n", (int)len));
+ }
+ else
+ {
+ ls->unread = 0;
+ ls->pos = NULL;
+ }
+ }
+ return result;
+}
+
+static int lbsLS_readbytes(
+ lbs_LoadState * ls,
+ unsigned char * buf,
+ size_t len
+ )
+{
+ const unsigned char * pos = lbsLS_eat(ls, len);
+ if (pos != NULL)
+ {
+ memcpy(buf, pos, len);
+ return LUABINS_ESUCCESS;
+ }
+ SPAM(("load: Failed to read %lu bytes\n", (unsigned long)len));
+ return LUABINS_EBADDATA;
+}
+
+static int load_value(lua_State * L, lbs_LoadState * ls);
+
+static int load_table(lua_State * L, lbs_LoadState * ls)
+{
+ int array_size = 0;
+ int hash_size = 0;
+ unsigned int total_size = 0;
+
+ int result = lbsLS_readbytes(ls, (unsigned char *)&array_size, LUABINS_LINT);
+ if (result == LUABINS_ESUCCESS)
+ {
+ result = lbsLS_readbytes(ls, (unsigned char *)&hash_size, LUABINS_LINT);
+ }
+
+ if (result == LUABINS_ESUCCESS)
+ {
+ total_size = array_size + hash_size;
+/*
+ SPAM((
+ "LT SIZE CHECK\n"
+ "* array_size %d limit 0 .. %d\n"
+ "* hash_size %d limit >0\n"
+ "* hash_size bytes %d, limit %d\n"
+ "* unread %u limit >min_size %u (total_size %u)\n",
+ array_size, MAXASIZE,
+ hash_size,
+ ceillog2((unsigned int)hash_size), MAXBITS,
+ (unsigned int)lbsLS_unread(ls),
+ (unsigned int)luabins_min_table_data_size(total_size),
+ (unsigned int)total_size
+ ));
+*/
+ if (
+ array_size < 0 || array_size > MAXASIZE ||
+ hash_size < 0 ||
+ (hash_size > 0 && ceillog2((unsigned int)hash_size) > MAXBITS) ||
+ lbsLS_unread(ls) < luabins_min_table_data_size(total_size)
+ )
+ {
+ result = LUABINS_EBADSIZE;
+ }
+ }
+
+ if (result == LUABINS_ESUCCESS)
+ {
+ unsigned int i = 0;
+
+ XSPAM((
+ "* load: creating table a:%d + h:%d = %d\n",
+ array_size, hash_size, total_size
+ ));
+
+ lua_createtable(L, array_size, hash_size);
+
+ for (i = 0; i < total_size; ++i)
+ {
+ int key_type = LUA_TNONE;
+
+ result = load_value(L, ls); /* Load key. */
+ if (result != LUABINS_ESUCCESS)
+ {
+ break;
+ }
+
+ /* Table key can't be nil or NaN */
+ key_type = lua_type(L, -1);
+ if (key_type == LUA_TNIL)
+ {
+ /* Corrupt data? */
+ SPAM(("load: nil as key detected\n"));
+ result = LUABINS_EBADDATA;
+ break;
+ }
+
+ if (key_type == LUA_TNUMBER)
+ {
+ lua_Number key = lua_tonumber(L, -1);
+ if (luai_numisnan(key))
+ {
+ /* Corrupt data? */
+ SPAM(("load: NaN as key detected\n"));
+ result = LUABINS_EBADDATA;
+ break;
+ }
+ }
+
+ result = load_value(L, ls); /* Load value. */
+ if (result != LUABINS_ESUCCESS)
+ {
+ break;
+ }
+
+ lua_rawset(L, -3);
+ }
+ }
+
+ return result;
+}
+
+static int load_value(lua_State * L, lbs_LoadState * ls)
+{
+ int result = LUABINS_ESUCCESS;
+ unsigned char type = lbsLS_readbyte(ls);
+ if (!lbsLS_good(ls))
+ {
+ SPAM(("load: Failed to read value type byte\n"));
+ return LUABINS_EBADDATA;
+ }
+
+ XSPAM(("* load: begin load_value\n"));
+
+ luaL_checkstack(L, 1, "load_value");
+
+ switch (type)
+ {
+ case LUABINS_CNIL:
+ XSPAM(("* load: nil\n"));
+ lua_pushnil(L);
+ break;
+
+ case LUABINS_CFALSE:
+ XSPAM(("* load: false\n"));
+ lua_pushboolean(L, 0);
+ break;
+
+ case LUABINS_CTRUE:
+ XSPAM(("* load: true\n"));
+ lua_pushboolean(L, 1);
+ break;
+
+ case LUABINS_CNUMBER:
+ {
+ lua_Number value;
+
+ XSPAM(("* load: number\n"));
+
+ result = lbsLS_readbytes(ls, (unsigned char *)&value, LUABINS_LNUMBER);
+ if (result == LUABINS_ESUCCESS)
+ {
+ lua_pushnumber(L, value);
+ }
+ }
+ break;
+
+ case LUABINS_CSTRING:
+ {
+ size_t len = 0;
+
+ XSPAM(("* load: string\n"));
+
+ result = lbsLS_readbytes(ls, (unsigned char *)&len, LUABINS_LSIZET);
+ if (result == LUABINS_ESUCCESS)
+ {
+ const unsigned char * pos = lbsLS_eat(ls, len);
+
+ XSPAM(("* load: string size %u\n", (int)len));
+
+ if (pos != NULL)
+ {
+ lua_pushlstring(L, (const char *)pos, len);
+ }
+ else
+ {
+ result = LUABINS_EBADSIZE;
+ }
+ }
+ }
+ break;
+
+ case LUABINS_CTABLE:
+ XSPAM(("* load: table\n"));
+ result = load_table(L, ls);
+ break;
+
+ default:
+ SPAM(("load: Unknown type char 0x%02X found\n", type));
+ result = LUABINS_EBADDATA;
+ break;
+ }
+
+ XSPAM(("* load: end load_value\n"));
+
+ return result;
+}
+
+int luabins_load(
+ lua_State * L,
+ const unsigned char * data,
+ size_t len,
+ int * count
+ )
+{
+ lbs_LoadState ls;
+ int result = LUABINS_ESUCCESS;
+ unsigned char num_items = 0;
+ int base = 0;
+ int i = 0;
+
+ base = lua_gettop(L);
+
+ lbsLS_init(&ls, data, len);
+ num_items = lbsLS_readbyte(&ls);
+ if (!lbsLS_good(&ls))
+ {
+ SPAM(("load: failed to read num_items byte\n"));
+ result = LUABINS_EBADDATA;
+ }
+ else if (num_items > LUABINS_MAXTUPLE)
+ {
+ SPAM(("load: tuple too large: %d\n", (int)num_items));
+ result = LUABINS_EBADSIZE;
+ }
+ else
+ {
+ XSPAM(("* load: tuple size %d\n", (int)num_items));
+ for (
+ i = 0;
+ i < num_items && result == LUABINS_ESUCCESS;
+ ++i
+ )
+ {
+ XSPAM(("* load: loading tuple item %d\n", i));
+ result = load_value(L, &ls);
+ }
+ }
+
+ if (result == LUABINS_ESUCCESS && lbsLS_unread(&ls) > 0)
+ {
+ SPAM(("load: %lu chars left at tail\n", lbsLS_unread(&ls)));
+ result = LUABINS_ETAILEFT;
+ }
+
+ if (result == LUABINS_ESUCCESS)
+ {
+ *count = num_items;
+ }
+ else
+ {
+ lua_settop(L, base); /* Discard intermediate results */
+ switch (result)
+ {
+ case LUABINS_EBADDATA:
+ lua_pushliteral(L, "can't load: corrupt data");
+ break;
+
+ case LUABINS_EBADSIZE:
+ lua_pushliteral(L, "can't load: corrupt data, bad size");
+ break;
+
+ case LUABINS_ETAILEFT:
+ lua_pushliteral(L, "can't load: extra data at end");
+ break;
+
+ default: /* Should not happen */
+ lua_pushliteral(L, "load failed");
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/vendor/luabins/src/luabins.cpp b/vendor/luabins/src/luabins.cpp
new file mode 100644
index 000000000..60668213f
--- /dev/null
+++ b/vendor/luabins/src/luabins.cpp
@@ -0,0 +1,104 @@
+/*
+* luabins.c
+* Luabins Lua module code
+* See copyright notice in luabins.h
+*/
+
+#include "luaheaders.h"
+
+#include "luabins.h"
+#include "saveload.h"
+
+/*
+* On success returns data string.
+* On failure returns nil and error message.
+*/
+static int l_save(lua_State * L)
+{
+ int error = luabins_save(L, 1, lua_gettop(L));
+ if (error == 0)
+ {
+ return 1;
+ }
+
+ lua_pushnil(L);
+ lua_replace(L, -3); /* Put nil before error message on stack */
+ return 2;
+}
+
+/*
+* On success returns true and loaded data tuple.
+* On failure returns nil and error message.
+*/
+static int l_load(lua_State * L)
+{
+ int count = 0;
+ int error = 0;
+ size_t len = 0;
+ const unsigned char * data = (const unsigned char *)luaL_checklstring(
+ L, 1, &len
+ );
+
+ lua_pushboolean(L, 1);
+
+ error = luabins_load(L, data, len, &count);
+ if (error == 0)
+ {
+ return count + 1;
+ }
+
+ lua_pushnil(L);
+ lua_replace(L, -3); /* Put nil before error message on stack */
+
+ return 2;
+}
+
+/* luabins Lua module API */
+static const struct luaL_reg R[] =
+{
+ { "save", l_save },
+ { "load", l_load },
+ { NULL, NULL }
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LUALIB_API int luaopen_luabins(lua_State * L)
+{
+ /*
+ * Compile-time checks for size constants.
+ * Consult PORTABILITY WARNING in saveload.h before changing constants.
+ */
+
+ /* int is too small on your platform, fix LUABINS_LINT */
+ luabins_static_assert(sizeof(int) >= LUABINS_LINT);
+ /* size_t is too small on your platform, fix LUABINS_LSIZET */
+ luabins_static_assert(sizeof(size_t) >= LUABINS_LSIZET);
+ /* unexpected lua_Number size, fix LUABINS_LNUMBER */
+ luabins_static_assert(sizeof(lua_Number) == LUABINS_LNUMBER);
+
+ /*
+ * Register module
+ */
+ luaL_register(L, "luabins", R);
+
+ /*
+ * Register module information
+ */
+ lua_pushliteral(L, LUABINS_VERSION);
+ lua_setfield(L, -2, "_VERSION");
+
+ lua_pushliteral(L, LUABINS_COPYRIGHT);
+ lua_setfield(L, -2, "_COPYRIGHT");
+
+ lua_pushliteral(L, LUABINS_DESCRIPTION);
+ lua_setfield(L, -2, "_DESCRIPTION");
+
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vendor/luabins/src/luabins.h b/vendor/luabins/src/luabins.h
new file mode 100644
index 000000000..e9e777773
--- /dev/null
+++ b/vendor/luabins/src/luabins.h
@@ -0,0 +1,69 @@
+/*
+* luabins.h
+* luabins -- Lua Binary Serialization Module
+* See copyright notice at the end of this file.
+*/
+
+#ifndef LUABINS_H_INCLUDED_
+#define LUABINS_H_INCLUDED_
+
+#define LUABINS_VERSION "Luabins 0.3"
+#define LUABINS_COPYRIGHT "Copyright (C) 2009-2010, Luabins authors"
+#define LUABINS_DESCRIPTION "Trivial Lua Binary Serialization Library"
+
+/* Define LUABINS_LUABUILTASCPP if your Lua is built as C++ */
+
+/* Can't be more than 255 */
+#define LUABINS_MAXTUPLE (250)
+
+#define LUABINS_MAXTABLENESTING (250)
+
+/*
+* Save Lua values from given state at given stack index range.
+* Lua value is left untouched. Note that empty range is not an error.
+* You may save from 0 to LUABINS_MAXTUPLE values.
+* Returns 0 on success, pushes saved data as a string on the top of stack.
+* Returns non-zero on failure, pushes error message on the top
+* of the stack.
+*/
+int luabins_save(lua_State * L, int index_from, int index_to);
+
+/*
+* Load Lua values from given byte chunk.
+* Returns 0 on success, pushes loaded values on stack.
+* Sets count to the number of values pushed.
+* Note that to have zero loaded items is a valid scenario.
+* Returns non-zero on failure, pushes error message on the top
+* of the stack.
+*/
+int luabins_load(
+ lua_State * L,
+ const unsigned char * data,
+ size_t len,
+ int * count
+ );
+
+/******************************************************************************
+* Copyright (C) 2009-2010 Luabins authors. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+#endif /* LUABINS_H_INCLUDED_ */
diff --git a/vendor/luabins/src/luaheaders.h b/vendor/luabins/src/luaheaders.h
new file mode 100644
index 000000000..5d1b4079b
--- /dev/null
+++ b/vendor/luabins/src/luaheaders.h
@@ -0,0 +1,14 @@
+#ifndef LUABINS_LUAHEADERS_H_INCLUDED_
+#define LUABINS_LUAHEADERS_H_INCLUDED_
+
+#if defined (__cplusplus) && !defined (LUABINS_LUABUILTASCPP)
+extern "C" {
+#endif
+
+#include
+#include
+#if defined (__cplusplus) && !defined (LUABINS_LUABUILTASCPP)
+}
+#endif
+
+#endif /* LUABINS_LUAHEADERS_H_INCLUDED_ */
diff --git a/vendor/luabins/src/luainternals.cpp b/vendor/luabins/src/luainternals.cpp
new file mode 100644
index 000000000..60d76edfe
--- /dev/null
+++ b/vendor/luabins/src/luainternals.cpp
@@ -0,0 +1,54 @@
+/*
+* luainternals.c
+* Code quoted from MIT-licensed Lua 5.1.4 internals
+* See copyright notice in lua.h
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include
+#include
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#include "luainternals.h"
+
+/*
+* BEGIN COPY-PASTE FROM Lua 5.1.4 llimits.h
+*/
+
+/* chars used as small naturals (so that `char' is reserved for characters) */
+typedef unsigned char lu_byte;
+
+/*
+* END COPY-PASTE FROM Lua 5.1.4 llimits.h
+*/
+
+/*
+* BEGIN COPY-PASTE FROM Lua 5.1.4 lobject.c
+*/
+
+int luaO_log2 (unsigned int x) {
+ static const lu_byte log_2[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = -1;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+
+}
+
+/*
+* END COPY-PASTE FROM Lua 5.1.4 lobject.c
+*/
diff --git a/vendor/luabins/src/luainternals.h b/vendor/luabins/src/luainternals.h
new file mode 100644
index 000000000..373b7e09f
--- /dev/null
+++ b/vendor/luabins/src/luainternals.h
@@ -0,0 +1,53 @@
+/*
+* luainternals.h
+* Code quoted from MIT-licensed Lua 5.1.4 internals
+* See copyright notice in lua.h
+*/
+
+#ifndef LUABINS_LUAINTERNALS_H_INCLUDED_
+#define LUABINS_LUAINTERNALS_H_INCLUDED_
+
+/*
+* BEGIN COPY-PASTE FROM Lua 5.1.4 luaconf.h
+* WARNING: If your Lua config differs, fix this!
+*/
+
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+
+/*
+* END COPY-PASTE FROM Lua 5.1.4 luaconf.h
+*/
+
+/*
+* BEGIN COPY-PASTE FROM Lua 5.1.4 lobject.h
+*/
+
+int luaO_log2 (unsigned int x);
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+/*
+* END COPY-PASTE FROM Lua 5.1.4 lobject.h
+*/
+
+/*
+* BEGIN COPY-PASTE FROM Lua 5.1.4 ltable.c
+*/
+
+/*
+** max size of array part is 2^MAXBITS
+*/
+#if LUAI_BITSINT > 26
+#define MAXBITS 26
+#else
+#define MAXBITS (LUAI_BITSINT-2)
+#endif
+
+#define MAXASIZE (1 << MAXBITS)
+
+/*
+* END COPY-PASTE FROM Lua 5.1.4 ltable.c
+*/
+
+#endif /* LUABINS_LUAINTERNALS_H_INCLUDED_ */
diff --git a/vendor/luabins/src/lualess.c b/vendor/luabins/src/lualess.c
new file mode 100644
index 000000000..fefc0a135
--- /dev/null
+++ b/vendor/luabins/src/lualess.c
@@ -0,0 +1,32 @@
+/*
+* lualess.h
+* Lua-related definitions for lua-less builds (based on Lua manual)
+* See copyright notice in luabins.h
+*/
+
+#include
+
+/*
+* lua_Alloc-compatible allocator to use in Lua-less applications
+* with lbs_SaveBuffer. Based on sample code from Lua 5.1 manual.
+*/
+void * lbs_simplealloc(
+ void * ud,
+ void * ptr,
+ size_t osize,
+ size_t nsize
+ )
+{
+ (void) ud;
+ (void) osize; /* not used */
+
+ if (nsize == 0)
+ {
+ free(ptr);
+ return NULL;
+ }
+ else
+ {
+ return realloc(ptr, nsize);
+ }
+}
diff --git a/vendor/luabins/src/lualess.h b/vendor/luabins/src/lualess.h
new file mode 100644
index 000000000..1e2a57f31
--- /dev/null
+++ b/vendor/luabins/src/lualess.h
@@ -0,0 +1,33 @@
+/*
+* lualess.h
+* Lua-related definitions for lua-less builds (based on Lua manual)
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_LUALESS_H_INCLUDED_
+#define LUABINS_LUALESS_H_INCLUDED_
+
+#include /* ptrdiff_t */
+
+/* WARNING: Change these if your luaconf.h has different settings */
+typedef double lua_Number;
+typedef ptrdiff_t lua_Integer;
+
+typedef void * (*lua_Alloc) (void *ud,
+ void *ptr,
+ size_t osize,
+ size_t nsize)
+ ;
+
+/*
+* lua_Alloc-compatible allocator to use in Lua-less applications
+* with lbs_SaveBuffer. Based on sample code from Lua 5.1 manual.
+*/
+void * lbs_simplealloc(
+ void * ud,
+ void * ptr,
+ size_t osize,
+ size_t nsize
+ );
+
+#endif /* LUABINS_LUALESS_H_INCLUDED_ */
diff --git a/vendor/luabins/src/save.cpp b/vendor/luabins/src/save.cpp
new file mode 100644
index 000000000..bf9b23e51
--- /dev/null
+++ b/vendor/luabins/src/save.cpp
@@ -0,0 +1,236 @@
+/*
+* save.c
+* Luabins save code
+* See copyright notice in luabins.h
+*/
+
+#include "luaheaders.h"
+
+#include "luabins.h"
+#include "saveload.h"
+#include "savebuffer.h"
+#include "write.h"
+
+/* TODO: Test this with custom allocator! */
+
+#if 0
+ #define SPAM(a) printf a
+#else
+ #define SPAM(a) (void)0
+#endif
+
+static int save_value(
+ lua_State * L,
+ luabins_SaveBuffer * sb,
+ int index,
+ int nesting
+ );
+
+/* Returns 0 on success, non-zero on failure */
+static int save_table(
+ lua_State * L,
+ luabins_SaveBuffer * sb,
+ int index,
+ int nesting
+ )
+{
+ int result = LUABINS_ESUCCESS;
+ int header_pos = 0;
+ int total_size = 0;
+
+ if (nesting > LUABINS_MAXTABLENESTING)
+ {
+ return LUABINS_ETOODEEP;
+ }
+
+ /* TODO: Hauling stack for key and value removal
+ may get too heavy for larger tables. Think out a better way.
+ */
+
+ header_pos = lbsSB_length(sb);
+ result = lbs_writeTableHeader(sb, 0, 0);
+
+ result = lbsSB_grow(sb, LUABINS_LINT + LUABINS_LINT);
+ if (result == LUABINS_ESUCCESS)
+ {
+ lua_checkstack(L, 2); /* Key and value */
+ lua_pushnil(L); /* key for lua_next() */
+ }
+
+ while (result == LUABINS_ESUCCESS && lua_next(L, index) != 0)
+ {
+ int value_pos = lua_gettop(L); /* We need absolute values */
+ int key_pos = value_pos - 1;
+
+ /* Save key. */
+ result = save_value(L, sb, key_pos, nesting);
+
+ /* Save value. */
+ if (result == LUABINS_ESUCCESS)
+ {
+ result = save_value(L, sb, value_pos, nesting);
+ }
+
+ if (result == LUABINS_ESUCCESS)
+ {
+ /* Remove value from stack, leave key for the next iteration. */
+ lua_pop(L, 1);
+ ++total_size;
+ }
+ }
+
+ if (result == LUABINS_ESUCCESS)
+ {
+ /*
+ Note that if array has holes, lua_objlen() may report
+ larger than actual array size. So we need to adjust.
+
+ TODO: Note inelegant downsize from size_t to int.
+ Handle integer overflow here.
+ */
+ int array_size = luabins_min(total_size, (int)lua_objlen(L, index));
+ int hash_size = luabins_max(0, total_size - array_size);
+
+ result = lbs_writeTableHeaderAt(sb, header_pos, array_size, hash_size);
+ }
+
+ return result;
+}
+
+/* Returns 0 on success, non-zero on failure */
+static int save_value(
+ lua_State * L,
+ luabins_SaveBuffer * sb,
+ int index,
+ int nesting
+ )
+{
+ int result = LUABINS_ESUCCESS;
+
+ switch (lua_type(L, index))
+ {
+ case LUA_TNIL:
+ result = lbs_writeNil(sb);
+ break;
+
+ case LUA_TBOOLEAN:
+ result = lbs_writeBoolean(sb, lua_toboolean(L, index));
+ break;
+
+ case LUA_TNUMBER:
+ result = lbs_writeNumber(sb, lua_tonumber(L, index));
+ break;
+
+ case LUA_TSTRING:
+ {
+ size_t len = 0;
+ const char * buf = lua_tolstring(L, index, &len);
+
+ result = lbs_writeString(sb, buf, len);
+ }
+ break;
+
+ case LUA_TTABLE:
+ result = save_table(L, sb, index, nesting + 1);
+ break;
+
+ case LUA_TNONE:
+ case LUA_TFUNCTION:
+ case LUA_TTHREAD:
+ case LUA_TUSERDATA:
+ default:
+ result = LUABINS_EBADTYPE;
+ }
+
+ return result;
+}
+
+int luabins_save(lua_State * L, int index_from, int index_to)
+{
+ unsigned char num_to_save = 0;
+ int index = index_from;
+ int base = lua_gettop(L);
+ luabins_SaveBuffer sb;
+
+ /*
+ * TODO: If lua_error() would happen below, would leak the buffer.
+ */
+
+ if (index_to - index_from > LUABINS_MAXTUPLE)
+ {
+ lua_pushliteral(L, "can't save that many items");
+ return LUABINS_EFAILURE;
+ }
+
+ /* Allowing to call luabins_save(L, 1, lua_gettop(L))
+ from C function, called from Lua with no arguments
+ (when lua_gettop() would return 0)
+ */
+ if (index_to < index_from)
+ {
+ index_from = 0;
+ index_to = 0;
+ num_to_save = 0;
+ }
+ else
+ {
+ if (
+ index_from < 0 || index_from > base ||
+ index_to < 0 || index_to > base
+ )
+ {
+ lua_pushliteral(L, "can't save: inexistant indices");
+ return LUABINS_EFAILURE;
+ }
+
+ num_to_save = index_to - index_from + 1;
+ }
+
+ {
+ void * alloc_ud = NULL;
+ lua_Alloc alloc_fn = lua_getallocf(L, &alloc_ud);
+ lbsSB_init(&sb, alloc_fn, alloc_ud);
+ }
+
+ lbs_writeTupleSize(&sb, num_to_save);
+ for ( ; index <= index_to; ++index)
+ {
+ int result = 0;
+
+ result = save_value(L, &sb, index, 0);
+ if (result != LUABINS_ESUCCESS)
+ {
+ switch (result)
+ {
+ case LUABINS_EBADTYPE:
+ lua_pushliteral(L, "can't save: unsupported type detected");
+ break;
+
+ case LUABINS_ETOODEEP:
+ lua_pushliteral(L, "can't save: nesting is too deep");
+ break;
+
+ case LUABINS_ETOOLONG:
+ lua_pushliteral(L, "can't save: not enough memory");
+ break;
+
+ default: /* Should not happen */
+ lua_pushliteral(L, "save failed");
+ break;
+ }
+
+ lbsSB_destroy(&sb);
+
+ return result;
+ }
+ }
+
+ {
+ size_t len = 0UL;
+ const unsigned char * buf = lbsSB_buffer(&sb, &len);
+ lua_pushlstring(L, (const char *)buf, len);
+ lbsSB_destroy(&sb);
+ }
+
+ return LUABINS_ESUCCESS;
+}
diff --git a/vendor/luabins/src/savebuffer.cpp b/vendor/luabins/src/savebuffer.cpp
new file mode 100644
index 000000000..27ab236cc
--- /dev/null
+++ b/vendor/luabins/src/savebuffer.cpp
@@ -0,0 +1,252 @@
+/*
+* savebuffer.c
+* Luabins save buffer
+* See copyright notice in luabins.h
+*/
+
+#include /* memcpy() */
+
+#include "luaheaders.h"
+
+#include "saveload.h"
+#include "savebuffer.h"
+
+#if 0
+ #define SPAM(a) printf a
+#else
+ #define SPAM(a) (void)0
+#endif
+
+/* Minimum allocation size */
+#define LUABINS_SAVEMINALLOC (256)
+
+/* TODO: Test this with custom allocator! */
+
+void lbsSB_init(
+ luabins_SaveBuffer * sb,
+ lua_Alloc alloc_fn,
+ void * alloc_ud
+ )
+{
+ sb->alloc_fn = alloc_fn;
+ sb->alloc_ud = alloc_ud;
+
+ sb->buffer = NULL;
+ sb->buf_size = 0UL;
+
+ sb->end = 0UL;
+}
+
+/*
+* Ensures that there is at least delta size available in buffer.
+* New size is aligned by blockSize increments
+* Returns non-zero if resize failed.
+* If you pre-sized the buffer, subsequent writes up to the new size
+* are guaranteed to not fail.
+*/
+int lbsSB_grow(luabins_SaveBuffer * sb, size_t delta)
+{
+ size_t needed_size = sb->end + delta;
+
+ if (needed_size > sb->buf_size)
+ {
+ /*
+ * Growth factor x1.5
+ * Discussion on possible values:
+ * http://stackoverflow.com/questions/2269063/buffer-growth-strategy
+ */
+
+ /* TODO: Handle size_t overflow? */
+
+ size_t new_size = 0;
+
+ size_t add_size = sb->buf_size / 2;
+ if (add_size < LUABINS_SAVEMINALLOC)
+ {
+ add_size = LUABINS_SAVEMINALLOC;
+ }
+
+ new_size = sb->buf_size + add_size;
+
+/*
+ SPAM((
+ "trying %lu + %lu = %lu (needed %lu)\n",
+ sb->buf_size,
+ add_size,
+ new_size,
+ needed_size
+ ));
+*/
+
+ while (new_size < needed_size)
+ {
+ SPAM(("...+%lu not enough, growing more\n", add_size));
+ /* Grow exponentially as needed */
+ add_size += new_size / 2;
+ new_size += add_size;
+ }
+
+ SPAM((
+ "growing from %lu to %lu (needed %lu)\n",
+ sb->buf_size,
+ new_size,
+ needed_size
+ ));
+
+ sb->buffer = (unsigned char *)sb->alloc_fn(
+ sb->alloc_ud,
+ sb->buffer,
+ sb->buf_size,
+ new_size
+ );
+ if (sb->buffer == NULL)
+ {
+ /* TODO: We probably should free the buffer here */
+ sb->buf_size = 0UL;
+ sb->end = 0;
+ return LUABINS_ETOOLONG;
+ }
+
+ sb->buf_size = new_size;
+ }
+
+ return LUABINS_ESUCCESS;
+}
+
+/*
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+*/
+int lbsSB_write(
+ luabins_SaveBuffer * sb,
+ const unsigned char * bytes,
+ size_t length
+ )
+{
+ int result = lbsSB_grow(sb, length);
+ if (result != LUABINS_ESUCCESS)
+ {
+ return result;
+ }
+
+ memcpy(&sb->buffer[sb->end], bytes, length);
+ sb->end += length;
+
+ return LUABINS_ESUCCESS;
+}
+
+/*
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+* Convenience function.
+*/
+int lbsSB_writechar(
+ luabins_SaveBuffer * sb,
+ const unsigned char byte
+ )
+{
+ /* TODO: Shouldn't this be a macro? */
+ int result = lbsSB_grow(sb, 1);
+ if (result != LUABINS_ESUCCESS)
+ {
+ return result;
+ }
+
+ sb->buffer[sb->end] = byte;
+ sb->end++;
+
+ return LUABINS_ESUCCESS;
+}
+
+/*
+* If offset is greater than total length, data is appended to the end.
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+*/
+int lbsSB_overwrite(
+ luabins_SaveBuffer * sb,
+ size_t offset,
+ const unsigned char * bytes,
+ size_t length
+ )
+{
+ if (offset > sb->end)
+ {
+ offset = sb->end;
+ }
+
+ if (offset + length > sb->end)
+ {
+ int result = lbsSB_grow(sb, length);
+ if (result != LUABINS_ESUCCESS)
+ {
+ return result;
+ }
+
+ sb->end = offset + length;
+ }
+
+ memcpy(&sb->buffer[offset], bytes, length);
+
+ return LUABINS_ESUCCESS;
+}
+
+/*
+* If offset is greater than total length, data is appended to the end.
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+* Convenience function.
+*/
+int lbsSB_overwritechar(
+ luabins_SaveBuffer * sb,
+ size_t offset,
+ unsigned char byte
+ )
+{
+ if (offset > sb->end)
+ {
+ offset = sb->end;
+ }
+
+ if (offset + 1 > sb->end)
+ {
+ int result = lbsSB_grow(sb, 1);
+ if (result != LUABINS_ESUCCESS)
+ {
+ return result;
+ }
+
+ sb->end = offset + 1;
+ }
+
+ sb->buffer[offset] = byte;
+
+ return LUABINS_ESUCCESS;
+}
+
+/*
+* Returns a pointer to the internal buffer with data.
+* Note that buffer is NOT zero-terminated.
+* Buffer is valid until next operation with the given sb.
+*/
+const unsigned char * lbsSB_buffer(luabins_SaveBuffer * sb, size_t * length)
+{
+ if (length != NULL)
+ {
+ *length = sb->end;
+ }
+ return sb->buffer;
+}
+
+void lbsSB_destroy(luabins_SaveBuffer * sb)
+{
+ if (sb->buffer != NULL)
+ {
+ /* Ignoring errors */
+ SPAM(("dealloc size %lu\n", sb->buf_size));
+ sb->alloc_fn(sb->alloc_ud, sb->buffer, sb->buf_size, 0UL);
+ sb->buffer = NULL;
+ sb->buf_size = 0UL;
+ sb->end = 0UL;
+ }
+}
diff --git a/vendor/luabins/src/savebuffer.h b/vendor/luabins/src/savebuffer.h
new file mode 100644
index 000000000..7207c3819
--- /dev/null
+++ b/vendor/luabins/src/savebuffer.h
@@ -0,0 +1,91 @@
+/*
+* savebuffer.h
+* Luabins save buffer
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_SAVEBUFFER_H_INCLUDED_
+#define LUABINS_SAVEBUFFER_H_INCLUDED_
+
+typedef struct luabins_SaveBuffer
+{
+ lua_Alloc alloc_fn;
+ void * alloc_ud;
+
+ unsigned char * buffer;
+ size_t buf_size;
+ size_t end;
+
+} luabins_SaveBuffer;
+
+void lbsSB_init(
+ luabins_SaveBuffer * sb,
+ lua_Alloc alloc_fn,
+ void * alloc_ud
+ );
+
+/*
+* Ensures that there is at least delta size available in buffer.
+* New size is aligned by blockSize increments.
+* Returns non-zero if resize failed.
+* If you pre-sized the buffer, subsequent writes up to the new size
+* are guaranteed to not fail.
+*/
+int lbsSB_grow(luabins_SaveBuffer * sb, size_t delta);
+
+/*
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+*/
+int lbsSB_write(
+ luabins_SaveBuffer * sb,
+ const unsigned char * bytes,
+ size_t length
+ );
+
+/*
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+* Convenience function.
+*/
+int lbsSB_writechar(
+ luabins_SaveBuffer * sb,
+ unsigned char byte
+ );
+
+#define lbsSB_length(sb) ( (sb)->end )
+
+/*
+* If offset is greater than total length, data is appended to the end.
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+*/
+int lbsSB_overwrite(
+ luabins_SaveBuffer * sb,
+ size_t offset,
+ const unsigned char * bytes,
+ size_t length
+ );
+
+/*
+* If offset is greater than total length, data is appended to the end.
+* Returns non-zero if write failed.
+* Allocates buffer as needed.
+* Convenience function.
+*/
+int lbsSB_overwritechar(
+ luabins_SaveBuffer * sb,
+ size_t offset,
+ unsigned char byte
+ );
+
+/*
+* Returns a pointer to the internal buffer with data.
+* Note that buffer is NOT zero-terminated.
+* Buffer is valid until next operation with the given sb.
+*/
+const unsigned char * lbsSB_buffer(luabins_SaveBuffer * sb, size_t * length);
+
+void lbsSB_destroy(luabins_SaveBuffer * sb);
+
+#endif /* LUABINS_SAVEBUFFER_H_INCLUDED_ */
diff --git a/vendor/luabins/src/saveload.h b/vendor/luabins/src/saveload.h
new file mode 100644
index 000000000..117e209ff
--- /dev/null
+++ b/vendor/luabins/src/saveload.h
@@ -0,0 +1,114 @@
+/*
+* saveload.h
+* Luabins internal constants and helper macros
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_SAVELOAD_H_INCLUDED_
+#define LUABINS_SAVELOAD_H_INCLUDED_
+
+/* Find minimum of two values */
+#define luabins_min(a, b) \
+ ( ((a) < (b)) ? (a) : (b) )
+
+/* Find maximum of two values */
+#define luabins_max(a, b) \
+ ( ((a) > (b)) ? (a) : (b) )
+
+/* Find minimum of three values */
+#define luabins_min3(a, b, c) \
+ ( ((a) < (b)) ? luabins_min((a), (c)) : luabins_min((b), (c)) )
+
+/* Preprocessor concatenation */
+#define LUABINS_CAT(a, b) a##b
+
+/* Static assert helper macro */
+#define luabins_static_assert_line(pred, line) \
+ typedef char LUABINS_CAT( \
+ static_assertion_failed_at_line_, \
+ line \
+ )[2 * !!(pred) - 1]
+
+/* Static (compile-time) assert */
+#define luabins_static_assert(pred) \
+ luabins_static_assert_line(pred, __LINE__)
+
+/* Internal error codes */
+#define LUABINS_ESUCCESS (0)
+#define LUABINS_EFAILURE (1)
+#define LUABINS_EBADTYPE (2)
+#define LUABINS_ETOODEEP (3)
+#define LUABINS_ENOSTACK (4)
+#define LUABINS_EBADDATA (5)
+#define LUABINS_ETAILEFT (6)
+#define LUABINS_EBADSIZE (7)
+#define LUABINS_ETOOLONG (8)
+
+/* Type bytes */
+#define LUABINS_CNIL '-' /* 0x2D (45) */
+#define LUABINS_CFALSE '0' /* 0x30 (48) */
+#define LUABINS_CTRUE '1' /* 0x31 (49) */
+#define LUABINS_CNUMBER 'N' /* 0x4E (78) */
+#define LUABINS_CSTRING 'S' /* 0x53 (83) */
+#define LUABINS_CTABLE 'T' /* 0x54 (84) */
+
+/*
+* PORTABILITY WARNING!
+* You have to ensure manually that length constants below are the same
+* for both code that does the save and code that does the load.
+* Also these constants must be actual or code below would break.
+* Beware of endianness and lua_Number actual type as well.
+* Note also that luabins does not check for overflow on save,
+* if your integer does not fit, it would be truncated.
+*/
+#define LUABINS_LINT (4)
+#define LUABINS_LSIZET (4)
+#define LUABINS_LNUMBER (8)
+
+/*
+* Derived lengths
+*
+* WARNING: Change these if format is changed!
+*/
+
+/* One type byte */
+#define LUABINS_LTYPEBYTE (1)
+
+/* Minimal table: type, array size, hash size, no data */
+#define LUABINS_LMINTABLE (LUABINS_LTYPEBYTE + LUABINS_LINT + LUABINS_LINT)
+
+/* Minimal number: type, number value */
+#define LUABINS_LMINNUMBER (LUABINS_LTYPEBYTE + LUABINS_LNUMBER)
+
+/* Minimal string: type, length, no data */
+#define LUABINS_LMINSTRING (LUABINS_LTYPEBYTE + LUABINS_LSIZET)
+
+/* Minimum large (non-boolean non-nil) value length */
+#define LUABINS_LMINLARGEVALUE \
+ ( luabins_min3(LUABINS_LMINTABLE, LUABINS_LMINSTRING, LUABINS_LMINSTRING) )
+
+/*
+* Lower limit on total table data size is determined as follows:
+* -- All entries are always key and value.
+* -- Minimum value size is one byte for nil and boolean,
+* but that is two keys maximum (nil can'be the key).
+* -- All the rest of key types are mimimum of LUABINS_MINLARGEVALUELEN
+* bytes (type byte plus data bytes).
+* -- All values in the table may be booleans.
+*
+* WARNING: Change this if format is changed!
+*
+* Note this formula does NOT take in account
+* table header (type byte and array/hash sizes).
+*/
+#define luabins_min_table_data_size(total_size) \
+ ( \
+ (total_size > 2) \
+ ? ( \
+ 2 * (LUABINS_LTYPEBYTE + LUABINS_LTYPEBYTE) \
+ + (total_size - 2) * (LUABINS_LMINLARGEVALUE + LUABINS_LTYPEBYTE) \
+ ) \
+ : (total_size * (LUABINS_LTYPEBYTE + LUABINS_LTYPEBYTE)) \
+ )
+
+#endif /* LUABINS_SAVELOAD_H_INCLUDED_ */
diff --git a/vendor/luabins/src/write.cpp b/vendor/luabins/src/write.cpp
new file mode 100644
index 000000000..996267ee4
--- /dev/null
+++ b/vendor/luabins/src/write.cpp
@@ -0,0 +1,75 @@
+/*
+* write.c
+* Luabins Lua-less write API
+* See copyright notice in luabins.h
+*/
+
+#include "luaheaders.h"
+
+#include "write.h"
+
+int lbs_writeTableHeaderAt(
+ luabins_SaveBuffer * sb,
+ size_t offset, /* Pass LUABINS_APPEND to append to the end of buffer */
+ int array_size,
+ int hash_size
+ )
+{
+ int result = lbsSB_grow(sb, 1 + LUABINS_LINT + LUABINS_LINT);
+ if (result == LUABINS_ESUCCESS)
+ {
+ /*
+ * We have to reset offset here in case it was beyond the buffer.
+ * Otherwise sequental overwrites may break.
+ */
+
+ size_t length = lbsSB_length(sb);
+ if (offset > length)
+ {
+ offset = length;
+ }
+
+ lbsSB_overwritechar(sb, offset, LUABINS_CTABLE);
+ lbsSB_overwrite(
+ sb,
+ offset + 1,
+ (const unsigned char *)&array_size,
+ LUABINS_LINT
+ );
+ lbsSB_overwrite(
+ sb,
+ offset + 1 + LUABINS_LINT,
+ (const unsigned char *)&hash_size,
+ LUABINS_LINT
+ );
+ }
+
+ return result;
+}
+
+int lbs_writeNumber(luabins_SaveBuffer * sb, lua_Number value)
+{
+ int result = lbsSB_grow(sb, 1 + LUABINS_LNUMBER);
+ if (result == LUABINS_ESUCCESS)
+ {
+ lbsSB_writechar(sb, LUABINS_CNUMBER);
+ lbsSB_write(sb, (const unsigned char *)&value, LUABINS_LNUMBER);
+ }
+ return result;
+}
+
+int lbs_writeString(
+ luabins_SaveBuffer * sb,
+ const char * value,
+ size_t length
+ )
+{
+ int result = lbsSB_grow(sb, 1 + LUABINS_LSIZET + length);
+ if (result == LUABINS_ESUCCESS)
+ {
+ lbsSB_writechar(sb, LUABINS_CSTRING);
+ lbsSB_write(sb, (const unsigned char *)&length, LUABINS_LSIZET);
+ lbsSB_write(sb, (const unsigned char *)value, length);
+ }
+ return result;
+}
diff --git a/vendor/luabins/src/write.h b/vendor/luabins/src/write.h
new file mode 100644
index 000000000..deb3e4996
--- /dev/null
+++ b/vendor/luabins/src/write.h
@@ -0,0 +1,44 @@
+/*
+* write.h
+* Luabins Lua-less write API
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_WRITE_H_INCLUDED_
+#define LUABINS_WRITE_H_INCLUDED_
+
+#include "saveload.h"
+#include "savebuffer.h"
+
+#define LUABINS_APPEND ((size_t)-1)
+
+#define lbs_writeTupleSize(sb, tuple_size) \
+ lbsSB_writechar((sb), (tuple_size))
+
+int lbs_writeTableHeaderAt(
+ luabins_SaveBuffer * sb,
+ size_t offset, /* Pass LUABINS_APPEND to append to the end of buffer */
+ int array_size,
+ int hash_size
+ );
+
+#define lbs_writeTableHeader(sb, array_size, hash_size) \
+ lbs_writeTableHeaderAt((sb), LUABINS_APPEND, (array_size), (hash_size))
+
+#define lbs_writeNil(sb) \
+ lbsSB_writechar((sb), LUABINS_CNIL)
+
+#define lbs_writeBoolean(sb, value) \
+ lbsSB_writechar((sb), ((value) == 0) ? LUABINS_CFALSE : LUABINS_CTRUE)
+
+int lbs_writeNumber(luabins_SaveBuffer * sb, lua_Number value);
+
+#define lbs_writeInteger lbs_writeNumber
+
+int lbs_writeString(
+ luabins_SaveBuffer * sb,
+ const char * value,
+ size_t length
+ );
+
+#endif /* LUABINS_WRITE_H_INCLUDED_ */
diff --git a/vendor/luabins/test/large_data.lua b/vendor/luabins/test/large_data.lua
new file mode 100644
index 000000000..29e212c95
--- /dev/null
+++ b/vendor/luabins/test/large_data.lua
@@ -0,0 +1,38 @@
+return true,nil,907.8738621938385904286406002938747406005859375,true,-10.074422490352031189786430331878364086151123046875,nil,0.5328851083912351516147509755683131515979766845703125,true,true,0.5577272826608863365294155300944112241268157958984375,0.77317682130875853285800758385448716580867767333984375,4203,"ΤФZkL+$ֽxG\000TKwj=h \"@5,WI.[\rN[bFV|ѽJVh1L\r{\\AuVcYN,ۛl C1O'\
+L\
+#kRD-UajmД)b'4:Ph_a&yLkjL|mX?Ub`Nxt.+{%ӿM,%Rߏ@v+z8\
+T$0Nd$#F::ptI>̣k52譂)*4:7'm+>UqD8","/IqOcw(R|gpž[*0uXEZ&Gx\r-? \000$qiʑ#9')vBpX |f^*hëk7z{l{(LOze@LZB7\r\000Nlt UVM\000fQ UtfC0SQSI2;[5a?~N4E,)\\|@$r⮣=?\\4VG=qh\"\r:ܪkZʚlmu`g↘$oFhqC%{̎s8Vh屍д}U#H4cֵ~\rEj-O j8uU¸nh_Ekx^xp3!6f#A.=ƵP\000Svח$\r1?n~3ֺ~MՏb9*XoZ~7^3-kgz[fGո0nFޥ6\000&5GM˔b̍ &","@E~1X`'2t qIpZ{h;8z`^\rޤ#,U1l퀟(1\
+̵gNL{o \\Pz|8~sh#o7*9;4~@gwIZi4All4Y@3UEyiK띍\
+ݲəq,;Եb>\000M܍!{E2!`焞{vbTy2Ow\\nnB(*m~eK>E,Q|8>[g*%36Ѯ#[qd%Pdթmr|j͇i6_ÒаG96X%dHN~-ՖNZAu :@eF9Cf4Tw2~a}\rR\rW_.G6=k$To|$1QFu7w\
+Yt^F;j-*É4qyVmv\"L{ hP/@WI:Ey}<#y! m_u\rRR^si `QZa?\\i YsX\r@50ܚ<ǽF~%rd<Op#ATd lEߪOG]#",false," 2}yI<;fJ#-1)mڟ6!¯\000(\r6s.Mϳ۠U{MLz (f6m{ɮJֲ,f[5J=Čf+~31 yOs&uѮ;JeE~?ѧ]Ea-ǥs#ӭ^d,t|10^'ҊK]mIW\000.ldt[+qh@Rqfy}a)ٖO\
+|6.fޟEoae~zqeJ>I`\\IwDᘩ(p+=gQ)K@.s_BO+n36\rD(3eOPL6'+7UYdoALiXb7\
+aH!p@lɤ\
+(tN&5tmu--\r$s5ߥAd@t͂8ZxRkn^K8S'i|=aiWy˹\000w(9W$dJ51T?LZL&4 5+;#T1p))>d_hfw^a\r}wT06EzB1sX3.XI^TwQœ+",nil,nil,false,{},"LpPh{,I*,C# =Ȋ\"csPt@GhqY+cBA~I ll6q~KCZNX.:L-9/X+fyg}gz멬Hå]qh:γ}L\"\\[4Ċ;\rMkp!X{yTo%`&GAӡ<ۨ+9nKÖԭسB`;pȲoP{N!خPng\"zlU4YE@B&",0.13656971796256012208203856062027625739574432373046875,"EeNd&IF>+nT[wNF۫D\"cX49(L>Ii7OaG&4)b3Ӗ9ate|GVgdhגzamH-g S\
+Kp5<_zQl\"F]}T6)l3oOc~XnHklea)F!\
+RmYwCh+BpYSO(S\
+&w/dpg2ؓuI+d@?ҏۈS89ߧFڃO\rkYǦ;IJLꍆ50_x~BdJ\
+bmY%i4QSA_$S^o\
+N>`vdߙP3-!uԁ/p+Ó#2y=]/!wު\000 O{5?rAFHho\000x#GC^xQMv俷oNggatF#,5(dFq-2đ9hm/ \\Hl)K(AN2Mz7?x{?Q}z\
+<$@ZwwWlSaI3.G-)mD妼p)vHGn\refm\
+M!MFX{hLPƏEִ<`?0]\000{LIQ먄UTFߏlj{vQ67yuWYFRʄU?I","̗7C&!ʑ quSCP ͐vHV6TuX\"#wjlhҾ^H]5Nڹ(pB9PJW>{;ފ?DgjiEȺ9!x.DXU#'[^ _4nSe6؎qט2*~J63kD=.^W.lSDh_LP4BSGWo#2{2J09͉k~@F4!g)^\\B\000s:@B.B20ڸ\000",0.4478582876957293290587358569609932601451873779296875,9428,nil,nil,1244.468268439391749780043028295040130615234375,"`ORH'L\ry×;a+4Ccf{qn~PT0][zƾDp8j.)(z,b̊ݾQw2Jw7-f#V|\rmm!0`1=3p}Y\000S.woV~izB+e,Z1FsPef#pwLx$NVR Lov3?ѷFX}:\\9kkpUZ˲QL9l#s{1Bz>2\
+oi\
+XEӹRgl=-_)zIʒ:P^ZjS\
+j/6mX_HҨ%04(mw4U\r6TJ>w DY(<]&I])F,HB?b_a2;Y&O+X|\"*2:(S+${+Tq[\r8Bz5$fNٝWvA8M.jm-\
+5ZgrzPN^AtZD[>3A ?\r:i\
+Q'k^ȡ\".J~AOYm%\\ج7t\
+9{6&","fdqKJ6^BŽIJN )\\cD(յ2{i_G^\"4Y/z|Ȫ'q6jDH?\\\r)F?}R\\62ܜ&Na@mIv%\\@(̰-hdQ@d.|?Ba$I$b/!NgΧWK#5pfDъ&Q#-G gT|f4B8hR.G 3Й7@G=gG[e;c\000j0C9/",1268,false,"<¾\"ҍE\rFP%\000U_[Nk^y'$M%]][HKdcCgkC[@,N@f]םBBZVAwӴ-#LD_=,8byPlլ?M@4+N\
+Ij.rMZ%GJ[ߔhXNޱӹXH\\o`MsՓ-G","д7,fǸٖ 7wY)Z1]s-MƓ]Xmgiڸ4CUOBa(quIGx5'f@b$u <jdwBe>T{*j1\
+F\rg^JiȀn:G!x[E>c5LSIև\000HKb#34)0IΓijݤT-*\000D*\rVTS0I)eB<\r\r%72)r=(U@u\"[/;=`|upHUebW2?Y{3xoF1qˀw^9ΐܳ9nJrF!b8{IbV^Ĩ6c\000w8<.(o/wMOYv{갌5뇅):=+oM=ܡy:\
+fܜrd!S,9dJV&*0m:\\p{#\
+Cm}Jt^r}i\\Bu?2L9h}m7.T,>i>qswgO&ޚءvI@`[¥0W68`_6 \000_>x\"䨺\000Ur\rQm\rA\r<0n^ma$ JϤYQ\
+","ZL}F[]st%]~EYԼ\"w5kF>lƛց0JFk5W\ry],rI{G;+VG(OoR.?V`%\\/Q3,t#>ii.|\\*\r^B>UBfq^;C6 w{[_]?V#9f/br7g[Q;-{yLeF] 60ȍb({d,Yy\r]W8Ai\
+0@miΑx88{9&quRG\000X\000$ 6]w 'R(!m5+D2LDMlJUkZ_#hJ$lGrǤZ;VpQ@LS>1G\"f|?1̀ψhnY` 9/b7Ǭ4' qb<H\"Y,UX41 uׯqj\\xVlrK5I'Y0\rEx~<I?U9!\r2 /kǙNT)I{uؖjȦ.]7:8&OmiV7^EByxF߲ngg2b˾A)4GoCEԭ<#+WkC6Tgph.V\r\"SXo5Ao*Ns'^VhcO$_rx(Z- \r(߷}DW#$@ )F%!m?:N(~DTk*a]b~I$ڮ`_ҖuMRV4P1UcЁ.",7463,7066,"/Ϲd Q뫛k9>PC#<*@`ȻI_P^EiH~dgVDeGY(>EGAMZRd*OGwy'T`f㛯kMS4ٹvxLbA}Q`o\"@#p㥐^m4V[6xAhMU\"xb9CPAh~N˟PFu PtcBoV,L~3g[k`st7;dŬKx%c};UZIkHĀ:,R^g3ܢL\\\\xb]z1dW\\|k[6JYP*Vݑ@Z~dNyp;LvoTXx`3y\000Fqzd䵰'?{C֓БOX# 8_bۀ|/RJO-nHk5$+$f5R!t6X9?6TsJL"
\ No newline at end of file
diff --git a/vendor/luabins/test/large_data.luabins b/vendor/luabins/test/large_data.luabins
new file mode 100644
index 000000000..edbba9f8b
Binary files /dev/null and b/vendor/luabins/test/large_data.luabins differ
diff --git a/vendor/luabins/test/test.c b/vendor/luabins/test/test.c
new file mode 100644
index 000000000..0a371458c
--- /dev/null
+++ b/vendor/luabins/test/test.c
@@ -0,0 +1,25 @@
+/*
+* test.c
+* Luabins test suite
+* See copyright notice in luabins.h
+*/
+
+#include
+
+#include "test.h"
+
+int main()
+{
+#ifdef __cplusplus
+ printf("luabins C API test compiled as C++\n");
+#else
+ printf("luabins C API test compiled as plain C\n");
+#endif /* __cplusplus */
+
+ test_savebuffer();
+ test_write_api();
+ test_fwrite_api();
+ test_api();
+
+ return 0;
+}
diff --git a/vendor/luabins/test/test.h b/vendor/luabins/test/test.h
new file mode 100644
index 000000000..3cb43e0fb
--- /dev/null
+++ b/vendor/luabins/test/test.h
@@ -0,0 +1,25 @@
+/*
+* test.h
+* Luabins test basics
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_TEST_H_INCLUDED_
+#define LUABINS_TEST_H_INCLUDED_
+
+#define STRINGIZE(s) #s
+
+#define TEST(name, body) \
+ static void name() \
+ { \
+ printf("---> BEGIN %s\n", STRINGIZE(name)); \
+ body \
+ printf("---> OK\n"); \
+ }
+
+void test_savebuffer();
+void test_write_api();
+void test_fwrite_api();
+void test_api();
+
+#endif /* LUABINS_TEST_H_INCLUDED_ */
diff --git a/vendor/luabins/test/test.lua b/vendor/luabins/test/test.lua
new file mode 100644
index 000000000..a5a42e976
--- /dev/null
+++ b/vendor/luabins/test/test.lua
@@ -0,0 +1,813 @@
+-- ----------------------------------------------------------------------------
+-- test.lua
+-- Luabins test suite
+-- See copyright notice in luabins.h
+-- ----------------------------------------------------------------------------
+
+package.cpath = "./?.so;"..package.cpath
+
+local randomseed = 1235134892
+--local randomseed = os.time()
+
+print("===== BEGIN LUABINS TEST SUITE (seed " .. randomseed .. ") =====")
+math.randomseed(randomseed)
+
+-- ----------------------------------------------------------------------------
+-- Utility functions
+-- ----------------------------------------------------------------------------
+
+local invariant = function(v)
+ return function()
+ return v
+ end
+end
+
+local escape_string = function(str)
+ return str:gsub(
+ "[^0-9A-Za-z_%- :]",
+ function(c)
+ return ("%%%02X"):format(c:byte())
+ end
+ )
+end
+
+local ensure_equals = function(msg, actual, expected)
+ if actual ~= expected then
+ error(
+ msg..":\n actual: `"..escape_string(tostring(actual))
+ .."`\nexpected: `"..escape_string(tostring(expected)).."'"
+ )
+ end
+end
+
+local ensure_equals_permute
+do
+ -- Based on MIT-licensed
+ -- http://snippets.luacode.org/sputnik.lua?p=snippets/ \
+ -- Iterator_over_Permutations_of_a_Table_62
+ -- Which is based on PiL
+ local function permgen(a, n, fn)
+ if n == 0 then
+ fn(a)
+ else
+ for i = 1, n do
+ -- put i-th element as the last one
+ a[n], a[i] = a[i], a[n]
+
+ -- generate all permutations of the other elements
+ permgen(a, n - 1, fn)
+
+ -- restore i-th element
+ a[n], a[i] = a[i], a[n]
+ end
+ end
+ end
+
+ --- an iterator over all permutations of the elements of a list.
+ -- Please note that the same list is returned each time,
+ -- so do not keep references!
+ -- @param a list-like table
+ -- @return an iterator which provides the next permutation as a list
+ local function permute_iter(a, n)
+ local n = n or #a
+ local co = coroutine.create(function() permgen(a, n, coroutine.yield) end)
+ return function() -- iterator
+ local code, res = coroutine.resume(co)
+ return res
+ end
+ end
+
+ ensure_equals_permute = function(
+ msg,
+ actual,
+ expected_prefix,
+ expected_body,
+ expected_suffix,
+ expected_body_size
+ )
+ expected_body_size = expected_body_size or #expected_body
+
+ local expected
+ for t in permute_iter(expected_body, expected_body_size) do
+ expected = expected_prefix .. table.concat(t) .. expected_suffix
+ if actual == expected then
+ return actual
+ end
+ end
+
+ error(
+ msg..":\nactual: `"..escape_string(tostring(actual))
+ .."`\nexpected one of permutations: `"
+ ..escape_string(tostring(expected)).."'"
+ )
+ end
+end
+
+local function deepequals(lhs, rhs)
+ if type(lhs) ~= "table" or type(rhs) ~= "table" then
+ return lhs == rhs
+ end
+
+ local checked_keys = {}
+
+ for k, v in pairs(lhs) do
+ checked_keys[k] = true
+ if not deepequals(v, rhs[k]) then
+ return false
+ end
+ end
+
+ for k, v in pairs(rhs) do
+ if not checked_keys[k] then
+ return false -- extra key
+ end
+ end
+
+ return true
+end
+
+local nargs = function(...)
+ return select("#", ...), ...
+end
+
+local pack = function(...)
+ return select("#", ...), { ... }
+end
+
+local eat_true = function(t, ...)
+ if t == nil then
+ error("failed: " .. (...))
+ end
+ return ...
+end
+
+-- ----------------------------------------------------------------------------
+-- Test helper functions
+-- ----------------------------------------------------------------------------
+
+local luabins_local = require 'luabins'
+assert(luabins_local == luabins)
+
+assert(type(luabins.save) == "function")
+assert(type(luabins.load) == "function")
+
+local check_load_fn_ok = function(eq, saved, ...)
+ local expected = { nargs(...) }
+ local loaded = { nargs(eat_true(luabins.load(saved))) }
+
+ ensure_equals("num arguments match", loaded[1], expected[1])
+ for i = 2, expected[1] do
+ assert(eq(loaded[i], expected[i]))
+ end
+
+ return saved
+end
+
+local check_load_ok = function(saved, ...)
+ return check_load_fn_ok(deepequals, saved, ...)
+end
+
+local check_fn_ok = function(eq, ...)
+ local saved = assert(luabins.save(...))
+
+ assert(type(saved) == "string")
+
+ print("saved length", #saved, "(display truncated to 70 chars)")
+ print(escape_string(saved):sub(1, 70))
+
+ return check_load_fn_ok(eq, saved, ...)
+end
+
+local check_ok = function(...)
+ print("check_ok")
+ return check_fn_ok(deepequals, ...)
+end
+
+local check_fail_save = function(msg, ...)
+ print("check_fail_save")
+ local res, err = luabins.save(...)
+ ensure_equals("result", res, nil)
+ ensure_equals("error message", err, msg)
+-- print("/check_fail_save")
+end
+
+local check_fail_load = function(msg, v)
+ print("check_fail_load")
+ local res, err = luabins.load(v)
+ ensure_equals("result", res, nil)
+ ensure_equals("error message", err, msg)
+-- print("/check_fail_load")
+end
+
+print("===== BEGIN LARGE DATA OK =====")
+
+-- Based on actual bug.
+-- This dataset triggered Lua C data stack overflow.
+-- (Note that bug is not triggered if check_ok is used)
+-- Update data with
+-- $ lua etc/toluabins.lua test/large_data.lua>test/large_data.luabins
+-- WARNING: Keep this test above other tests, so Lua stack is small.
+assert(
+ luabins.load(
+ assert(io.open("test/large_data.luabins", "r"):read("*a"))
+ )
+ )
+
+print("===== LARGE DATA OK =====")
+
+-- ----------------------------------------------------------------------------
+-- Basic tests
+-- ----------------------------------------------------------------------------
+
+print("===== BEGIN BASIC TESTS =====")
+
+print("---> basic corrupt data tests")
+
+check_fail_load("can't load: corrupt data", "")
+check_fail_load("can't load: corrupt data", "bad data")
+
+print("---> basic extra data tests")
+do
+ local s
+
+ s = check_ok()
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(nil)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(true)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(false)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(42)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(math.pi)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(1/0)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok(-1/0)
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok("Luabins")
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok({ })
+
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ s = check_ok({ a = 1, 2 })
+ check_fail_load("can't load: extra data at end", s .. "-")
+end
+
+print("---> basic type tests")
+
+-- This is the way to detect NaN
+check_fn_ok(function(lhs, rhs) return lhs ~= lhs and rhs ~= rhs end, 0/0)
+
+check_ok("")
+
+check_ok("Embedded\0Zero")
+
+check_ok(("longstring"):rep(1024000))
+
+check_fail_save("can't save: unsupported type detected", function() end)
+check_fail_save(
+ "can't save: unsupported type detected",
+ coroutine.create(function() end)
+ )
+check_fail_save("can't save: unsupported type detected", newproxy())
+
+print("---> basic table tests")
+
+check_ok({ 1 })
+check_ok({ a = 1 })
+check_ok({ a = 1, 2, [42] = true, [math.pi] = math.huge })
+check_ok({ { } })
+check_ok({ a = {}, b = { c = 7 } })
+check_ok({ 1, 2, 3 })
+check_ok({ [1] = 1, [1.5] = 2, [2] = 3 })
+check_ok({ 1, nil, 3 })
+check_ok({ 1, nil, 3, [{ 1, nil, 3 }] = { 1, nil, 3 } })
+
+print("---> basic tuple tests")
+
+check_ok(nil, nil)
+
+do
+ local s = check_ok(nil, false, true, 42, "Embedded\0Zero", { { [{3}] = 54 } })
+ check_fail_load("can't load: extra data at end", s .. "-")
+
+ check_ok(check_ok(s)) -- Save data string couple of times more
+end
+
+print("---> basic table tuple tests")
+
+check_ok({ a = {}, b = { c = 7 } }, nil, { { } }, 42)
+
+check_ok({ ["1"] = "str", [1] = "num" })
+
+check_ok({ [true] = true })
+check_ok({ [true] = true, [false] = false, 1 })
+
+print("---> basic fail save tests")
+
+check_fail_save(
+ "can't save: unsupported type detected",
+ { { function() end } }
+ )
+
+check_fail_save(
+ "can't save: unsupported type detected",
+ nil, false, true, 42, "Embedded\0Zero", function() end,
+ { { [{3}] = 54 } }
+ )
+
+print("---> recursive table test")
+
+local t = {}; t[1] = t
+check_fail_save("can't save: nesting is too deep", t)
+
+print("---> metatable test")
+
+check_ok(setmetatable({}, {__index = function(t, k) return k end}))
+
+print("===== BASIC TESTS OK =====")
+
+print("===== BEGIN FORMAT SANITY TESTS =====")
+
+-- Format sanity checks for LJ2 compatibility tests.
+-- These tests are intended to help debugging actual problems
+-- of test suite, and are not feature complete.
+-- What is not checked here, checked in the rest of suite.
+
+do
+ do
+ local saved = check_ok(1)
+ local expected =
+ "\001".."N"
+ .. "\000\000\000\000\000\000\240\063" -- Note number is a double
+
+ ensure_equals(
+ "1 as number",
+ expected,
+ saved
+ )
+ end
+
+ do
+ local saved = check_ok({ [true] = 1 })
+ local expected =
+ "\001".."T"
+ .. "\000\000\000\000".."\001\000\000\000"
+ .. "1"
+ .. "N\000\000\000\000\000\000\240\063" -- Note number is a double
+
+ ensure_equals(
+ "1 as value",
+ expected,
+ saved
+ )
+ end
+
+ do
+ local saved = check_ok({ [1] = true })
+ local expected =
+ "\001".."T"
+ .. "\001\000\000\000".."\000\000\000\000"
+ .. "N\000\000\000\000\000\000\240\063" -- Note number is a double
+ .. "1"
+
+ ensure_equals(
+ "1 as key",
+ expected,
+ saved
+ )
+ end
+end
+
+print("===== FORMAT SANITY TESTS OK =====")
+
+print("===== BEGIN AUTOCOLLAPSE TESTS =====")
+
+-- Note: those are ad-hoc tests, tuned for old implementation
+-- which generated save data on Lua stack.
+-- These tests are kept here for performance comparisons.
+
+local LUABINS_CONCATTHRESHOLD = 1024
+
+local gen_t = function(size)
+ -- two per numeric entry, three per string entry,
+ -- two entries per key-value pair
+ local actual_size = math.ceil(size / (2 + 3))
+ print("generating table of "..actual_size.." pairs")
+ local t = {}
+ for i = 1, actual_size do
+ t[i] = "a"..i
+ end
+ return t
+end
+
+-- Test table value autocollapse
+check_ok(gen_t(LUABINS_CONCATTHRESHOLD - 100)) -- underflow, no autocollapse
+check_ok(gen_t(LUABINS_CONCATTHRESHOLD)) -- autocollapse, no extra elements
+check_ok(gen_t(LUABINS_CONCATTHRESHOLD + 100)) -- autocollapse, extra elements
+
+-- Test table key autocollapse
+check_ok({ [gen_t(LUABINS_CONCATTHRESHOLD - 4)] = true })
+
+-- Test multiarg autocollapse
+check_ok(
+ 1,
+ gen_t(LUABINS_CONCATTHRESHOLD - 5),
+ 2,
+ gen_t(LUABINS_CONCATTHRESHOLD - 5),
+ 3
+ )
+
+print("===== AUTOCOLLAPSE TESTS OK =====")
+
+print("===== BEGIN MIN TABLE SIZE TESTS =====")
+
+do
+ -- one small key
+ do
+ local data = { [true] = true }
+ local saved = check_ok(data)
+ ensure_equals(
+ "format sanity check",
+ "\001".."T".."\000\000\000\000".."\001\000\000\000".."11",
+ saved
+ )
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ saved:sub(1, #saved - 1)
+ )
+
+ -- As long as array and hash size sum is correct
+ -- (and both are within limits), load is successful.
+ -- If values are swapped, we get some performance hit.
+ check_load_ok(
+ "\001".."T".."\001\000\000\000".."\000\000\000\000".."11",
+ data
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T".."\001\000\000\000".."\001\000\000\000".."11"
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T".."\000\000\000\000".."\002\000\000\000".."11"
+ )
+
+ check_fail_load(
+ "can't load: extra data at end",
+ "\001".."T".."\000\000\000\000".."\000\000\000\000".."11"
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T".."\255\255\255\255".."\255\255\255\255".."11"
+ )
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T".."\000\255\255\255".."\000\255\255\255".."11"
+ )
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\255".."T".."\000\000\000\000".."\000\000\000\000"
+ )
+ end
+
+ -- two small keys
+ do
+ local data = { [true] = true, [false] = false }
+ local saved = check_ok({ [true] = true, [false] = false })
+ ensure_equals_permute(
+ "format sanity check",
+ saved,
+ "\001" .. "T" .. "\000\000\000\000" .. "\002\000\000\000",
+ {
+ "0" .. "0";
+ "1" .. "1";
+ },
+ ""
+ )
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ saved:sub(1, #saved - 1)
+ )
+
+ -- See above about swapped array and hash sizes
+ check_load_ok(
+ "\001".."T".."\001\000\000\000".."\001\000\000\000".."1100",
+ data
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T".."\000\000\000\000".."\003\000\000\000".."0011"
+ )
+ end
+
+ -- two small and one large key
+ do
+ local saved = check_ok({ [true] = true, [false] = false, [1] = true })
+ ensure_equals_permute(
+ "format sanity check",
+ saved,
+ "\001" .. "T" .. "\001\000\000\000" .. "\002\000\000\000",
+ {
+ "0" .. "0";
+ "1" .. "1";
+ -- Note number is a double
+ "N\000\000\000\000\000\000\240\063" .. "1";
+ },
+ ""
+ )
+
+ check_fail_load(
+ "can't load: corrupt data",
+ saved:sub(1, #saved - 1)
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T"
+ .. "\002\000\000\000".."\002\000\000\000"
+ .. "0011"
+ .. "N\000\000\000\000\000\000\240\063"
+ .. "1"
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T"
+ .. "\001\000\000\000".."\003\000\000\000"
+ .. "0011"
+ .. "N\000\000\000\000\000\000\240\063"
+ .. "1"
+ )
+ end
+
+ -- two small and two large keys
+ do
+ local saved = check_ok(
+ { [true] = true, [false] = false, [1] = true, [42] = true }
+ )
+ local expected =
+ "\001".."T"
+ .. "\001\000\000\000".."\003\000\000\000"
+ .. "0011"
+ ensure_equals_permute(
+ "format sanity check",
+ saved,
+ "\001" .. "T" .. "\001\000\000\000" .. "\003\000\000\000",
+ {
+ "0" .. "0";
+ "1" .. "1";
+ "N\000\000\000\000\000\000\069\064" .. "1";
+ "N\000\000\000\000\000\000\240\063" .. "1";
+ },
+ ""
+ )
+
+ check_fail_load(
+ "can't load: corrupt data",
+ saved:sub(1, #saved - 1)
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T"
+ .. "\001\000\000\000".."\005\000\000\000"
+ .. "0011"
+ .. "N\000\000\000\000\000\000\069\064"
+ .. "1"
+ .. "N\000\000\000\000\000\000\240\063"
+ .. "1"
+ )
+
+ check_fail_load(
+ "can't load: corrupt data, bad size",
+ "\001".."T"
+ .. "\003\000\000\000".."\003\000\000\000"
+ .. "0011"
+ .. "N\000\000\000\000\000\000\069\064"
+ .. "1"
+ .. "N\000\000\000\000\000\000\240\063"
+ .. "1"
+ )
+ end
+end
+
+print("===== MIN TABLE SIZE TESTS OK =====")
+
+print("===== BEGIN LOAD TRUNCATION TESTS =====")
+
+local function gen_random_dataset(num, nesting)
+ num = num or math.random(0, 128)
+ nesting = nesting or 1
+
+ local gen_str = function()
+ local t = {}
+ local n = math.random(0, 1024)
+ for i = 1, n do
+ t[i] = string.char(math.random(0, 255))
+ end
+ return table.concat(t)
+ end
+
+ local gen_bool = function() return math.random() >= 0.5 end
+
+ local gen_nil = function() return nil end
+
+ local generators =
+ {
+ gen_nil;
+ gen_nil;
+ gen_nil;
+ gen_bool;
+ gen_bool;
+ gen_bool;
+ function() return math.random() end;
+ function() return math.random(-10000, 10000) end;
+ function() return math.random() * math.random(-10000, 10000) end;
+ gen_str;
+ gen_str;
+ gen_str;
+ function()
+ if nesting >= 24 then
+ return nil
+ end
+
+ local t = {}
+ local n = math.random(0, 24 - nesting)
+ for i = 1, n do
+ local k = gen_random_dataset(1, nesting + 1)
+ if k == nil then
+ k = "(nil)"
+ end
+ t[ k ] = gen_random_dataset(
+ 1,
+ nesting + 1
+ )
+ end
+
+ return t
+ end;
+ }
+
+ local t = {}
+ for i = 1, num do
+ local n = math.random(1, #generators)
+ t[i] = generators[n]()
+ end
+ return unpack(t, 0, num)
+end
+
+local random_dataset_num, random_dataset_data = pack(gen_random_dataset())
+local random_dataset_saved = check_ok(
+ unpack(random_dataset_data, 0, random_dataset_num)
+ )
+
+local num_tries = 100
+local errors = {}
+for i = 1, num_tries do
+ local to = math.random(1, #random_dataset_saved - 1)
+ local new_data = random_dataset_saved:sub(1, to)
+
+ local res, err = luabins.load(new_data)
+ ensure_equals("truncated data must not be loaded", res, nil)
+ errors[err] = (errors[err] or 0) + 1
+end
+
+print("truncation errors encountered:")
+for err, n in pairs(errors) do
+ print(err, n)
+end
+
+print("===== BASIC LOAD TRUNCATION OK =====")
+
+print("===== BEGIN LOAD MUTATION TESTS =====")
+
+local function mutate_string(str, num, override)
+ num = num or math.random(1, 8)
+
+ if num < 1 then
+ return str
+ end
+
+ local mutators =
+ {
+ -- truncate at end
+ function(str)
+ local pos = math.random(1, #str)
+ return str:sub(1, pos)
+ end;
+ -- truncate at beginning
+ function(str)
+ local pos = math.random(1, #str)
+ return str:sub(-pos)
+ end;
+ -- cut out the middle
+ function(str)
+ local from = math.random(1, #str)
+ local to = math.random(from, #str)
+ return str:sub(1, from) .. str:sub(to)
+ end;
+ -- swap two halves
+ function(str)
+ local pos = math.random(1, #str)
+ return str:sub(pos + 1, #str) .. str:sub(1, pos)
+ end;
+ -- swap two characters
+ function(str)
+ local pa, pb = math.random(1, #str), math.random(1, #str)
+ local a, b = str:sub(pa, pa), str:sub(pb, pb)
+ return
+ str:sub(1, pa - 1) ..
+ a ..
+ str:sub(pa + 1, pb - 1) ..
+ b ..
+ str:sub(pb + 1, #str)
+ end;
+ -- replace one character
+ function(str)
+ local pos = math.random(1, #str)
+ return
+ str:sub(1, pos - 1) ..
+ string.char(math.random(0, 255)) ..
+ str:sub(pos + 1, #str)
+ end;
+ -- increase one character
+ function(str)
+ local pos = math.random(1, #str)
+ local b = str:byte(pos, pos) + 1
+ if b > 255 then
+ b = 0
+ end
+ return
+ str:sub(1, pos - 1) ..
+ string.char(b) ..
+ str:sub(pos + 1, #str)
+ end;
+ -- decrease one character
+ function(str)
+ local pos = math.random(1, #str)
+ local b = str:byte(pos, pos) - 1
+ if b < 0 then
+ b = 255
+ end
+ return
+ str:sub(1, pos - 1) ..
+ string.char(b) ..
+ str:sub(pos + 1, #str)
+ end;
+ }
+
+ local n = override or math.random(1, #mutators)
+
+ str = mutators[n](str)
+
+ return mutate_string(str, num - 1, override)
+end
+
+local num_tries = 100000
+local num_successes = 0
+local errors = {}
+for i = 1, num_tries do
+ local new_data = mutate_string(random_dataset_saved)
+
+ local res, err = luabins.load(new_data)
+ if res == nil then
+ errors[err] = (errors[err] or 0) + 1
+ else
+ num_successes = num_successes + 1
+ end
+end
+
+if num_successes == 0 then
+ print("no mutated strings loaded successfully")
+else
+ -- This is ok, since we may corrupt data, not format.
+ -- If it is an issue for user, he must append checksum to data,
+ -- as usual.
+ print("mutated strings loaded successfully: "..num_successes)
+end
+
+print("mutation errors encountered:")
+for err, n in pairs(errors) do
+ print(err, n)
+end
+
+print("===== BASIC LOAD MUTATION OK =====")
+
+print("OK")
diff --git a/vendor/luabins/test/test_api.c b/vendor/luabins/test/test_api.c
new file mode 100644
index 000000000..75a1bda45
--- /dev/null
+++ b/vendor/luabins/test/test_api.c
@@ -0,0 +1,311 @@
+/*
+* test_api.c
+* Luabins API tests
+* See copyright notice in luabins.h
+*/
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#include "luabins.h"
+
+#define STACKGUARD "-- stack ends here --"
+
+/* Note this one does not dump values to protect from embedded zeroes. */
+static int dump_lua_stack(lua_State * L, int base)
+{
+ int top = lua_gettop(L);
+
+ if (top == 0)
+ {
+ lua_pushliteral(L, "-- stack is empty --");
+ }
+ else
+ {
+ int pos = 0;
+ luaL_Buffer b;
+
+ luaL_buffinit(L, &b);
+
+ for (pos = top; pos > 0; --pos)
+ {
+ luaL_addstring(&b, (pos != base) ? "[" : "{");
+ lua_pushinteger(L, pos);
+ luaL_addvalue(&b);
+ luaL_addstring(&b, (pos != base) ? "] - " : "} -");
+ luaL_addstring(&b, luaL_typename(L, pos));
+ luaL_addstring(&b, "\n");
+ }
+
+ luaL_pushresult(&b);
+ }
+
+ if (lua_gettop(L) != top + 1)
+ {
+ return luaL_error(L, "dumpstack not balanced %d %d", top, lua_gettop(L));
+ }
+
+ return 1;
+}
+
+static void fatal(lua_State * L, const char * msg)
+{
+ dump_lua_stack(L, 0);
+ fprintf(stderr, "%s\nSTACK\n%s", msg, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ fflush(stderr);
+ exit(1);
+}
+
+static void check(lua_State * L, int base, int extra)
+{
+ int top = lua_gettop(L);
+ if (top != base + extra)
+ {
+ fatal(L, "stack unbalanced");
+ }
+
+ lua_pushliteral(L, STACKGUARD);
+ if (lua_rawequal(L, -1, base) == 0)
+ {
+ fatal(L, "stack guard corrupted");
+ }
+ lua_pop(L, 1);
+}
+
+static void checkerr(lua_State * L, int base, const char * err)
+{
+ int top = lua_gettop(L);
+ if (top != base + 1)
+ {
+ fatal(L, "stack unbalanced on error");
+ }
+
+ lua_pushliteral(L, STACKGUARD);
+ if (lua_rawequal(L, -1, base) == 0)
+ {
+ fatal(L, "stack guard corrupted");
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, err);
+ if (lua_rawequal(L, -1, -2) == 0)
+ {
+ fprintf(stderr, "actual: '%s'\n", lua_tostring(L, -2));
+ fprintf(stderr, "expected: '%s'\n", err);
+ fatal(L, "error message mismatch");
+ }
+ lua_pop(L, 2); /* Pops error message as well */
+}
+
+static int push_testdataset(lua_State * L)
+{
+ int base = lua_gettop(L);
+
+ lua_pushnil(L);
+ lua_pushboolean(L, 0);
+ lua_pushboolean(L, 1);
+ lua_pushinteger(L, 42);
+ lua_pushliteral(L, "luabins");
+
+ lua_newtable(L);
+
+ if (lua_gettop(L) - base != 6)
+ {
+ fatal(L, "push_dataset broken");
+ }
+
+ return 6;
+}
+
+static void check_testdataset_on_top(lua_State * L)
+{
+ int base = lua_gettop(L);
+
+ /* TODO: Check table contents */
+ if (!lua_istable(L, -1))
+ {
+ fatal(L, "dataset (-1) is not table");
+ }
+
+ lua_pushliteral(L, "luabins");
+ if (!lua_rawequal(L, -1, -2 - 1))
+ {
+ fatal(L, "dataset (-2) value mismatch");
+ }
+ lua_pop(L, 1);
+
+ lua_pushinteger(L, 42);
+ if (!lua_rawequal(L, -1, -3 - 1))
+ {
+ fatal(L, "dataset (-3) value mismatch");
+ }
+ lua_pop(L, 1);
+
+ lua_pushboolean(L, 1);
+ if (!lua_rawequal(L, -1, -4 - 1))
+ {
+ fatal(L, "dataset (-4) value mismatch");
+ }
+ lua_pop(L, 1);
+
+ lua_pushboolean(L, 0);
+ if (!lua_rawequal(L, -1, -5 - 1))
+ {
+ fatal(L, "dataset (-5) value mismatch");
+ }
+ lua_pop(L, 1);
+
+ lua_pushnil(L);
+ if (!lua_rawequal(L, -1, -6 - 1))
+ {
+ fatal(L, "dataset (-6) value mismatch");
+ }
+ lua_pop(L, 1);
+
+ if (lua_gettop(L) != base)
+ {
+ fatal(L, "check_dataset_on_top broken");
+ }
+}
+
+void test_api()
+{
+ int base = 0;
+ int count = 0;
+ const unsigned char * str;
+ size_t length = 0;
+
+ lua_State * L = lua_open();
+ luaL_openlibs(L);
+
+ printf("---> BEGIN test_api\n");
+
+ /* Push stack check value */
+ lua_pushliteral(L, STACKGUARD);
+ base = lua_gettop(L);
+
+ /* Sanity check */
+ check(L, base, 0);
+
+ /* Save error: inexistant index */
+ if (luabins_save(L, lua_gettop(L) + 1, lua_gettop(L) + 1) == 0)
+ {
+ fatal(L, "save should fail");
+ }
+
+ checkerr(L, base, "can't save: inexistant indices");
+
+ if (luabins_save(L, -1, -1) == 0)
+ {
+ fatal(L, "save should fail");
+ }
+
+ checkerr(L, base, "can't save: inexistant indices");
+
+ /* Assuming other save errors to be tested in test.lua */
+
+ /* Trigger load error */
+
+ if (luabins_load(L, (const unsigned char *)"", 0, &count) == 0)
+ {
+ fatal(L, "load should fail");
+ }
+
+ checkerr(L, base, "can't load: corrupt data");
+
+ /* Assuming other load errors to be tested in test.lua */
+
+ /* Do empty save */
+ if (luabins_save(L, base, base - 1) != 0)
+ {
+ fatal(L, "empty save failed");
+ }
+ check(L, base, 1);
+
+ str = (const unsigned char *)lua_tolstring(L, -1, &length);
+ if (str == NULL || length == 0)
+ {
+ fatal(L, "bad empty save string");
+ }
+
+ /* Load empty save */
+
+ if (luabins_load(L, str, length, &count) != 0)
+ {
+ fatal(L, "empty load failed");
+ }
+
+ if (count != 0)
+ {
+ fatal(L, "bad empty load count");
+ }
+
+ /* Pop saved data string */
+ check(L, base, 1);
+ lua_pop(L, 1);
+ check(L, base, 0);
+
+ {
+ /* Save test dataset */
+
+ int num_items = push_testdataset(L);
+ check(L, base, num_items);
+
+ if (luabins_save(L, base + 1, base + num_items) != 0)
+ {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ fatal(L, "test dataset save failed");
+ }
+
+ check(L, base, num_items + 1);
+
+ /* Load test dataset */
+
+ str = (const unsigned char *)lua_tolstring(L, -1, &length);
+ if (str == NULL || length == 0)
+ {
+ fatal(L, "bad empty save string");
+ }
+
+ if (luabins_load(L, str, length, &count) != 0)
+ {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ fatal(L, "test dataset load failed");
+ }
+
+ if (count != num_items)
+ {
+ fatal(L, "wrong test dataset load count");
+ }
+
+ check(L, base, num_items + 1 + num_items);
+
+ check_testdataset_on_top(L); /* Check loaded data */
+
+ lua_pop(L, 1 + num_items);
+
+ check_testdataset_on_top(L); /* Check original data intact */
+
+ lua_pop(L, num_items);
+
+ check(L, base, 0);
+
+ /* Assuming further tests are done in test.lua */
+ }
+
+ lua_close(L);
+
+ printf("---> OK\n");
+}
diff --git a/vendor/luabins/test/test_fwrite_api.c b/vendor/luabins/test/test_fwrite_api.c
new file mode 100644
index 000000000..68123ef82
--- /dev/null
+++ b/vendor/luabins/test/test_fwrite_api.c
@@ -0,0 +1,112 @@
+/*
+* test_fwrite_api.c
+* Luabins Lua-less fwrite API tests
+* See copyright notice in luabins.h
+*/
+
+/*
+* WARNING: This suite is format-specific. Change it when format changes.
+*/
+
+#include
+#include
+#include
+
+#include "lualess.h"
+#include "fwrite.h"
+
+#include "test.h"
+#include "util.h"
+
+/******************************************************************************/
+
+/*
+* Note it is different from test_savebuffer variant.
+* We're interested in higher level stuff here.
+*/
+static void check_buffer(
+ FILE * f,
+ const char * expected_buf_c,
+ size_t expected_length
+ )
+{
+ const unsigned char * expected_buf = (unsigned char *)expected_buf_c;
+ unsigned char * actual_buf = NULL;
+ size_t actual_length = ftell(f);
+ size_t actually_read = 0;
+
+ fseek(f, 0, SEEK_SET);
+
+ actual_buf = (unsigned char *)malloc(actual_length);
+ actually_read = fread(actual_buf, actual_length, 1, f);
+ if (actually_read != 1ul)
+ {
+ fprintf(
+ stderr,
+ "fread count error: got %lu, expected %lu\n",
+ actually_read, 1ul
+ );
+
+ free(actual_buf);
+ fclose(f);
+ exit(1);
+ }
+
+ fseek(f, actual_length, SEEK_SET);
+
+ if (actual_length != expected_length)
+ {
+ fprintf(
+ stderr,
+ "length mismatch: got %lu, expected %lu\n",
+ actual_length, expected_length
+ );
+ fprintf(stderr, "actual:\n");
+ fprintbuf(stderr, actual_buf, actual_length);
+ fprintf(stderr, "expected:\n");
+ fprintbuf(stderr, expected_buf, expected_length);
+
+ free(actual_buf);
+ fclose(f);
+ exit(1);
+ }
+
+ if (memcmp(actual_buf, expected_buf, expected_length) != 0)
+ {
+ fprintf(stderr, "buffer mismatch\n");
+ fprintf(stderr, "actual:\n");
+ fprintbuf(stderr, actual_buf, actual_length);
+ fprintf(stderr, "expected:\n");
+ fprintbuf(stderr, expected_buf, expected_length);
+
+ free(actual_buf);
+ fclose(f);
+ exit(1);
+ }
+
+ free(actual_buf);
+}
+
+/******************************************************************************/
+
+#define CAT(a, b) a ## b
+
+#define TEST_NAME(x) CAT(test_fwrite, x)
+#define CALL_NAME(x) CAT(lbs_fwrite, x)
+#define BUFFER_NAME (f)
+#define INIT_BUFFER \
+ FILE * f = tmpfile();
+
+#define DESTROY_BUFFER \
+ fclose(f);
+
+#define CHECK_BUFFER check_buffer
+
+#include "write_tests.inc"
+
+/******************************************************************************/
+
+void test_fwrite_api()
+{
+ RUN_GENERATED_TESTS;
+}
diff --git a/vendor/luabins/test/test_savebuffer.c b/vendor/luabins/test/test_savebuffer.c
new file mode 100644
index 000000000..877ee5c4c
--- /dev/null
+++ b/vendor/luabins/test/test_savebuffer.c
@@ -0,0 +1,499 @@
+/*
+* test_savebuffer.c
+* Luabins SaveBuffer tests
+* See copyright notice in luabins.h
+*/
+
+/*
+* TODO: Tests are tuned for old fixed-increment memory allocation strategy.
+* Test for exponential growth corner-cases specifically.
+*/
+
+#include
+#include
+#include
+
+#include "lualess.h"
+#include "savebuffer.h"
+
+#include "test.h"
+
+/******************************************************************************/
+
+static size_t NOT_CHANGED = (size_t)-1;
+static void * NOT_CHANGED_PTR = NULL;
+
+static size_t DUMMY = (size_t)-42;
+static void * DUMMY_PTR = NULL;
+
+static void * g_last_ud = NULL;
+static size_t g_last_osize = 0;
+
+static void reset_alloc_globals()
+{
+ g_last_ud = NOT_CHANGED_PTR;
+ g_last_osize = NOT_CHANGED;
+}
+
+static void init_globals()
+{
+ NOT_CHANGED_PTR = (void *)&NOT_CHANGED;
+ DUMMY_PTR = (void *)&DUMMY;
+
+ reset_alloc_globals();
+}
+
+static void * dummy_alloc(
+ void * ud,
+ void * ptr,
+ size_t osize,
+ size_t nsize
+ )
+{
+ g_last_ud = ud;
+ g_last_osize = osize;
+
+ if (nsize == 0)
+ {
+ free(ptr);
+ return NULL;
+ }
+ else
+ {
+ return realloc(ptr, nsize);
+ }
+}
+
+/******************************************************************************/
+
+static void check_alloc(void * expected_ud, size_t expected_osize)
+{
+ if (g_last_ud != expected_ud)
+ {
+ fprintf(
+ stderr,
+ "userdata mismatch in allocator: got %p, expected %p\n",
+ g_last_ud, expected_ud
+ );
+ exit(1);
+ }
+
+ if (g_last_osize != expected_osize)
+ {
+ fprintf(
+ stderr,
+ "old size mismatch in allocator: got %lu, expected %lu\n",
+ g_last_osize, expected_osize
+ );
+ exit(1);
+ }
+
+ reset_alloc_globals();
+}
+
+static void check_buffer(
+ luabins_SaveBuffer * sb,
+ const char * expected_buf_c,
+ size_t expected_length,
+ void * expected_ud,
+ size_t expected_osize
+ )
+{
+ const unsigned char * expected_buf = (const unsigned char *)expected_buf_c;
+
+ {
+ size_t actual_length = lbsSB_length(sb);
+ if (actual_length != expected_length)
+ {
+ fprintf(
+ stderr,
+ "lbsSB_length mismatch in allocator: got %lu, expected %lu\n",
+ actual_length, expected_length
+ );
+ exit(1);
+ }
+ }
+
+ {
+ size_t actual_length = (size_t)-1;
+ const unsigned char * actual_buf = lbsSB_buffer(sb, &actual_length);
+ if (actual_length != expected_length)
+ {
+ fprintf(
+ stderr,
+ "lsbSB_buffer length mismatch in allocator: got %lu, expected %lu\n",
+ actual_length, expected_length
+ );
+ exit(1);
+ }
+
+ if (memcmp(actual_buf, expected_buf, expected_length) != 0)
+ {
+ fprintf(
+ stderr,
+ "lsbSB_buffer buf mismatch in allocator\n"
+ );
+ exit(1);
+ }
+ }
+
+ check_alloc(expected_ud, expected_osize);
+}
+
+/******************************************************************************/
+
+TEST (test_init_destroy,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ check_buffer(&sb, "", 0, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(NOT_CHANGED_PTR, NOT_CHANGED);
+})
+
+TEST (test_grow_zero,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 0);
+ check_buffer(&sb, "", 0, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(NOT_CHANGED_PTR, NOT_CHANGED);
+})
+
+TEST (test_grow_bufsiz,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 1024);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 1344);
+})
+
+TEST (test_grow_one,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 1);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_grow_one_grow_one_noop,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 1);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 0);
+
+ lbsSB_grow(&sb, 1);
+ check_buffer(&sb, "", 0, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_grow_one_grow_bufsiz_noop,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 1);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 0);
+
+ lbsSB_grow(&sb, 255);
+ check_buffer(&sb, "", 0, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_grow_one_grow_bufsiz_one,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_grow(&sb, 1);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 0);
+
+ lbsSB_grow(&sb, 257);
+ check_buffer(&sb, "", 0, DUMMY_PTR, 256);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 512);
+})
+
+/******************************************************************************/
+
+TEST (test_write_empty,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"", 0);
+ check_buffer(&sb, "", 0, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(NOT_CHANGED_PTR, NOT_CHANGED);
+})
+
+TEST (test_write,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"42", 3);
+ check_buffer(&sb, "42", 3, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_write_embedded_zero,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"4\02", 4);
+ check_buffer(&sb, "4\02", 4, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_write_write_smallsiz,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"01234567", 8);
+ check_buffer(&sb, "01234567", 8, DUMMY_PTR, 0);
+
+ lbsSB_write(&sb, (unsigned char*)"01234567", 8);
+ check_buffer(&sb, "0123456701234567", 8 + 8, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_write(&sb, (unsigned char*)"0123", 4);
+ check_buffer(
+ &sb,
+ "01234567012345670123",
+ 8 + 8 + 4,
+ NOT_CHANGED_PTR,
+ NOT_CHANGED
+ );
+
+ lbsSB_write(&sb, (unsigned char*)"0123456789ABCDEF", 16);
+ check_buffer(
+ &sb,
+ "012345670123456701230123456789ABCDEF",
+ 8 + 8 + 4 + 16,
+ NOT_CHANGED_PTR,
+ NOT_CHANGED
+ );
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+/******************************************************************************/
+
+TEST (test_writechar,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_writechar(&sb, 'A');
+ check_buffer(&sb, "A", 1, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_writechar_zero,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_writechar(&sb, '\0');
+ check_buffer(&sb, "\0", 1, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_write_writechar_smallsiz,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"01234567", 8);
+ check_buffer(&sb, "01234567", 8, DUMMY_PTR, 0);
+
+ lbsSB_writechar(&sb, 'A');
+ check_buffer(&sb, "01234567A", 8 + 1, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+/******************************************************************************/
+
+TEST (test_overwrite_empty,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_overwrite(&sb, 0, (unsigned char*)"42", 3);
+ check_buffer(&sb, "42", 3, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwrite_inplace,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"ABCD", 4);
+ check_buffer(&sb, "ABCD", 4, DUMMY_PTR, 0);
+
+ lbsSB_overwrite(&sb, 1, (unsigned char*)"42", 2);
+ check_buffer(&sb, "A42D", 4, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwrite_overflow,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"ABCD", 4);
+ check_buffer(&sb, "ABCD", 4, DUMMY_PTR, 0);
+
+ lbsSB_overwrite(&sb, 3, (unsigned char*)"42", 2);
+ check_buffer(&sb, "ABC42", 5, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwrite_overflow_grows,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"012345", 6);
+ check_buffer(&sb, "012345", 6, DUMMY_PTR, 0);
+
+ lbsSB_overwrite(&sb, 4, (unsigned char*)"ABCDEF", 6);
+ check_buffer(&sb, "0123ABCDEF", 10, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwrite_large_offset_appends,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"012345", 6);
+ check_buffer(&sb, "012345", 6, DUMMY_PTR, 0);
+
+ lbsSB_overwrite(&sb, 100, (unsigned char*)"ABCDEF", 6);
+ check_buffer(&sb, "012345ABCDEF", 12, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+/******************************************************************************/
+
+TEST (test_overwritechar_empty_buffer,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_overwritechar(&sb, 0, 'A');
+ check_buffer(&sb, "A", 1, DUMMY_PTR, 0);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwritechar_inplace,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"ABCD", 4);
+ check_buffer(&sb, "ABCD", 4, DUMMY_PTR, 0);
+
+ lbsSB_overwritechar(&sb, 1, '!');
+ check_buffer(&sb, "A!CD", 4, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+TEST (test_overwritechar_large_offset_appends,
+{
+ luabins_SaveBuffer sb;
+ lbsSB_init(&sb, dummy_alloc, DUMMY_PTR);
+
+ lbsSB_write(&sb, (unsigned char*)"01234567", 8);
+ check_buffer(&sb, "01234567", 8, DUMMY_PTR, 0);
+
+ lbsSB_overwritechar(&sb, 100, '!');
+ check_buffer(&sb, "01234567!", 9, NOT_CHANGED_PTR, NOT_CHANGED);
+
+ lbsSB_destroy(&sb);
+ check_alloc(DUMMY_PTR, 256);
+})
+
+/******************************************************************************/
+
+void test_savebuffer()
+{
+ init_globals();
+
+ test_init_destroy();
+ test_grow_zero();
+ test_grow_bufsiz();
+ test_grow_one();
+ test_grow_one_grow_one_noop();
+ test_grow_one_grow_bufsiz_noop();
+ test_grow_one_grow_bufsiz_one();
+
+ test_write_empty();
+ test_write();
+ test_write_embedded_zero();
+ test_write_write_smallsiz();
+
+ test_writechar();
+ test_writechar_zero();
+ test_write_writechar_smallsiz();
+
+ test_overwrite_empty();
+ test_overwrite_inplace();
+ test_overwrite_overflow();
+ test_overwrite_overflow_grows();
+ test_overwrite_large_offset_appends();
+
+ test_overwritechar_empty_buffer();
+ test_overwritechar_inplace();
+ test_overwritechar_large_offset_appends();
+}
diff --git a/vendor/luabins/test/test_write_api.c b/vendor/luabins/test/test_write_api.c
new file mode 100644
index 000000000..18bef2381
--- /dev/null
+++ b/vendor/luabins/test/test_write_api.c
@@ -0,0 +1,130 @@
+/*
+* test_write_api.c
+* Luabins Lua-less write API tests
+* See copyright notice in luabins.h
+*/
+
+/*
+* WARNING: This suite is format-specific. Change it when format changes.
+*/
+
+#include
+#include
+#include
+
+/* Should be included first */
+#include "lualess.h"
+#include "write.h"
+
+#include "test.h"
+#include "util.h"
+
+/******************************************************************************/
+
+/*
+* Note it is different from test_savebuffer variant.
+* We're interested in higher level stuff here.
+*/
+static void check_buffer(
+ luabins_SaveBuffer * sb,
+ const char * expected_buf_c,
+ size_t expected_length
+ )
+{
+ const unsigned char * expected_buf = (const unsigned char *)expected_buf_c;
+
+ size_t actual_length = (size_t)-1;
+ const unsigned char * actual_buf = lbsSB_buffer(sb, &actual_length);
+ if (actual_length != expected_length)
+ {
+ fprintf(
+ stderr,
+ "lsbSB_buffer length mismatch: got %lu, expected %lu\n",
+ actual_length, expected_length
+ );
+ fprintf(stderr, "actual:\n");
+ fprintbuf(stderr, actual_buf, actual_length);
+ fprintf(stderr, "expected:\n");
+ fprintbuf(stderr, expected_buf, expected_length);
+ exit(1);
+ }
+
+ if (memcmp(actual_buf, expected_buf, expected_length) != 0)
+ {
+ fprintf(stderr, "lsbSB_buffer buffer mismatch\n");
+ fprintf(stderr, "actual:\n");
+ fprintbuf(stderr, actual_buf, actual_length);
+ fprintf(stderr, "expected:\n");
+ fprintbuf(stderr, expected_buf, expected_length);
+
+ exit(1);
+ }
+}
+
+/******************************************************************************/
+
+#define CAT(a, b) a ## b
+
+#define TEST_NAME(x) CAT(test_write, x)
+#define CALL_NAME(x) CAT(lbs_write, x)
+#define BUFFER_NAME (&sb)
+#define INIT_BUFFER \
+ luabins_SaveBuffer sb; \
+ lbsSB_init(BUFFER_NAME, lbs_simplealloc, NULL);
+
+#define DESTROY_BUFFER \
+ lbsSB_destroy(BUFFER_NAME);
+
+#define CHECK_BUFFER check_buffer
+
+#include "write_tests.inc"
+
+/******************************************************************************/
+
+TEST (test_writeTableHeaderAt,
+{
+ INIT_BUFFER;
+
+ {
+ unsigned char tuple_size = 0x01;
+ int array_size = 0x00;
+ int hash_size = 0x00;
+ int table_header_pos = 0;
+
+ lbs_writeTupleSize(BUFFER_NAME, tuple_size);
+ table_header_pos = lbsSB_length(BUFFER_NAME);
+ lbs_writeTableHeader(BUFFER_NAME, array_size, hash_size);
+
+ CHECK_BUFFER(
+ &sb,
+ "\x01" "T" "\x00\x00\x00\x00" "\x00\x00\x00\x00",
+ 1 + 1 + 4 + 4
+ );
+
+ array_size = 0xAB;
+ hash_size = 0xCD;
+
+ lbs_writeTableHeaderAt(
+ BUFFER_NAME,
+ table_header_pos,
+ array_size,
+ hash_size
+ );
+ CHECK_BUFFER(
+ BUFFER_NAME,
+ "\x01" "T" "\xAB\x00\x00\x00" "\xCD\x00\x00\x00",
+ 1 + 1 + 4 + 4
+ );
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+void test_write_api()
+{
+ RUN_GENERATED_TESTS;
+
+ test_writeTableHeaderAt();
+}
diff --git a/vendor/luabins/test/util.c b/vendor/luabins/test/util.c
new file mode 100644
index 000000000..84b4ac6b7
--- /dev/null
+++ b/vendor/luabins/test/util.c
@@ -0,0 +1,17 @@
+/*
+* util.c
+* Luabins test utilities
+* See copyright notice in luabins.h
+*/
+
+#include "util.h"
+
+void fprintbuf(FILE * out, const unsigned char * b, size_t len)
+{
+ size_t i = 0;
+ for (i = 0; i < len; ++i)
+ {
+ fprintf(out, "%02X ", b[i]);
+ }
+ fprintf(out, "\n");
+}
diff --git a/vendor/luabins/test/util.h b/vendor/luabins/test/util.h
new file mode 100644
index 000000000..b5e7121ab
--- /dev/null
+++ b/vendor/luabins/test/util.h
@@ -0,0 +1,14 @@
+/*
+* util.h
+* Luabins test utilities
+* See copyright notice in luabins.h
+*/
+
+#ifndef LUABINS_TEST_UTIL_H_INCLUDED_
+#define LUABINS_TEST_UTIL_H_INCLUDED_
+
+#include
+
+void fprintbuf(FILE * out, const unsigned char * b, size_t len);
+
+#endif /* LUABINS_TEST_UTIL_H_INCLUDED_ */
diff --git a/vendor/luabins/test/write_tests.inc b/vendor/luabins/test/write_tests.inc
new file mode 100644
index 000000000..f3c215675
--- /dev/null
+++ b/vendor/luabins/test/write_tests.inc
@@ -0,0 +1,175 @@
+/*
+* write_tests.inc
+* Luabins Lua-less write API
+* See copyright notice in luabins.h
+*/
+
+/* Note this file intentionally does not have include guards */
+
+/*
+* Depends on following symbols:
+* -- TEST_NAME
+* -- CALL_NAME
+* -- BUFFER_NAME
+* -- INIT_BUFFER
+* -- DESTROY_BUFFER
+* -- CHECK_BUFFER
+*
+* Defines symbols
+* -- RUN_GENERATED_TESTS
+*/
+
+TEST (TEST_NAME(TupleSize),
+{
+ INIT_BUFFER;
+
+ {
+ unsigned char tuple_size = 0xAB;
+
+ CALL_NAME(TupleSize)(BUFFER_NAME, tuple_size);
+ CHECK_BUFFER(BUFFER_NAME, "\xAB", 1);
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(TableHeader),
+{
+ INIT_BUFFER;
+
+ {
+ int array_size = 0xAB;
+ int hash_size = 0xCD;
+
+ CALL_NAME(TableHeader)(BUFFER_NAME, array_size, hash_size);
+ CHECK_BUFFER(
+ BUFFER_NAME,
+ "T" "\xAB\x00\x00\x00" "\xCD\x00\x00\x00",
+ 1 + 4 + 4
+ );
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(Nil),
+{
+ INIT_BUFFER;
+
+ {
+ CALL_NAME(Nil)(BUFFER_NAME);
+ CHECK_BUFFER(BUFFER_NAME, "-", 1);
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(Boolean),
+{
+ INIT_BUFFER;
+
+ {
+ CALL_NAME(Boolean)(BUFFER_NAME, 1);
+ CHECK_BUFFER(BUFFER_NAME, "1", 1);
+
+ CALL_NAME(Boolean)(BUFFER_NAME, 0);
+ CHECK_BUFFER(BUFFER_NAME, "10", 1 + 1);
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(Number),
+{
+ INIT_BUFFER;
+
+ {
+ /* Note number is a double */
+ CALL_NAME(Number)(BUFFER_NAME, 1.0);
+ CHECK_BUFFER(BUFFER_NAME, "N" "\x00\x00\x00\x00\x00\x00\xF0\x3F", 1 + 8);
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(Integer),
+{
+ INIT_BUFFER;
+
+ {
+ /* Note integer is alsow written as a double */
+ CALL_NAME(Integer)(BUFFER_NAME, 1);
+ CHECK_BUFFER(BUFFER_NAME, "N" "\x00\x00\x00\x00\x00\x00\xF0\x3F", 1 + 8);
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+TEST (TEST_NAME(StringEmpty),
+{
+ INIT_BUFFER;
+
+ {
+ CALL_NAME(String)(BUFFER_NAME, "", 0);
+ CHECK_BUFFER(BUFFER_NAME, "S" "\x00\x00\x00\x00", 1 + 4);
+ }
+
+ DESTROY_BUFFER;
+})
+
+TEST (TEST_NAME(StringSimple),
+{
+ INIT_BUFFER;
+
+ {
+ CALL_NAME(String)(BUFFER_NAME, "Luabins", 7);
+ CHECK_BUFFER(
+ BUFFER_NAME,
+ "S" "\x07\x00\x00\x00" "Luabins",
+ 1 + 4 + 7
+ );
+ }
+
+ DESTROY_BUFFER;
+})
+
+TEST (TEST_NAME(StringEmbeddedZero),
+{
+ INIT_BUFFER;
+
+ {
+ CALL_NAME(String)(BUFFER_NAME, "Embedded\0Zero", 13);
+ CHECK_BUFFER(
+ BUFFER_NAME,
+ "S" "\x0D\x00\x00\x00" "Embedded\0Zero",
+ 1 + 4 + 13
+ );
+ }
+
+ DESTROY_BUFFER;
+})
+
+/******************************************************************************/
+
+#define RUN_GENERATED_TESTS \
+ TEST_NAME(TupleSize)(); \
+ TEST_NAME(TableHeader)(); \
+ TEST_NAME(Nil)(); \
+ TEST_NAME(Boolean)(); \
+ TEST_NAME(Number)(); \
+ TEST_NAME(Integer)(); \
+ TEST_NAME(StringEmpty)(); \
+ TEST_NAME(StringSimple)(); \
+ TEST_NAME(StringEmbeddedZero)();
diff --git a/vendor/luabins/tmp/.keepme b/vendor/luabins/tmp/.keepme
new file mode 100644
index 000000000..e69de29bb