From 8ee30955068cd894a58b031839b6cf84c690e840 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sat, 8 Mar 2014 19:14:22 -0800 Subject: [PATCH] Do disgusting things to make LuaAssFile work LuaAssFile relied on the lifetime guarantees of intrusive lists (i.e. you can pretty much do whatever the fuck you want and it'll work out), but AssInfo objects are no longer owned by an intrusive list. Do awful things to lazily copy the header section when needed to provide similar lifetime guarantees. --- aegisub/src/auto4_lua.h | 21 +++++- aegisub/src/auto4_lua_assfile.cpp | 119 +++++++++++++++++++++--------- 2 files changed, 101 insertions(+), 39 deletions(-) diff --git a/aegisub/src/auto4_lua.h b/aegisub/src/auto4_lua.h index fe0fdbb85..ec87b420c 100644 --- a/aegisub/src/auto4_lua.h +++ b/aegisub/src/auto4_lua.h @@ -73,19 +73,34 @@ namespace Automation4 { void CheckBounds(int idx); /// How ass file been modified by the script since the last commit - int modification_type; + int modification_type = 0; /// Reference count used to avoid deleting this until both lua and the /// calling C++ code are done with it - int references; + int references = 2; /// Set of subtitle lines being modified; initially a shallow copy of ass->Line std::vector lines; + bool script_info_copied = false; + /// Commits to apply once processing completes successfully std::deque pending_commits; /// Lines to delete once processing complete successfully std::vector> lines_to_delete; + /// Create copies of all of the lines in the script info section if it + /// hasn't already happened. This is done lazily, since it only needs + /// to happen when the user modifies the headers in some way, which + /// most runs of a script will not do. + void InitScriptInfoIfNeeded(); + /// Add the line at the given index to the list of lines to be deleted + /// when the script completes, unless it's an AssInfo, since those are + /// owned by the container. + void QueueLineForDeletion(size_t idx); + /// Set the line at the index to the given value + void AssignLine(size_t idx, std::unique_ptr e); + void InsertLine(std::vector &vec, size_t idx, std::unique_ptr e); + int ObjectIndexRead(lua_State *L); void ObjectIndexWrite(lua_State *L); int ObjectGetLen(lua_State *L); @@ -107,7 +122,7 @@ namespace Automation4 { static LuaAssFile *GetObjPointer(lua_State *L, int idx); /// makes a Lua representation of AssEntry and places on the top of the stack - static void AssEntryToLua(lua_State *L, AssEntry *e); + 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); diff --git a/aegisub/src/auto4_lua_assfile.cpp b/aegisub/src/auto4_lua_assfile.cpp index 3747b2f47..ec8332587 100644 --- a/aegisub/src/auto4_lua_assfile.cpp +++ b/aegisub/src/auto4_lua_assfile.cpp @@ -37,7 +37,6 @@ #include "auto4_lua.h" #include "auto4_lua_utils.h" -#include "ass_attachment.h" #include "ass_dialogue.h" #include "ass_info.h" #include "ass_file.h" @@ -50,8 +49,6 @@ #include #include -#include -#include #include #include @@ -118,8 +115,8 @@ namespace { int modification_mask(AssEntry *e) { - switch (e->Group()) - { + if (!e) return AssFile::COMMIT_SCRIPTINFO; + switch (e->Group()) { case AssEntryGroup::DIALOGUE: return AssFile::COMMIT_DIAG_ADDREM; case AssEntryGroup::STYLE: return AssFile::COMMIT_STYLES; default: return AssFile::COMMIT_SCRIPTINFO; @@ -142,19 +139,23 @@ namespace Automation4 { luaL_error(L, "Requested out-of-range line from subtitle file: %d", idx); } - void LuaAssFile::AssEntryToLua(lua_State *L, AssEntry *e) + void LuaAssFile::AssEntryToLua(lua_State *L, size_t idx) { lua_newtable(L); + const AssEntry *e = lines[idx]; + if (!e) + e = &ass->Info[idx]; + set_field(L, "section", e->GroupHeader()); - if (AssInfo *info = dynamic_cast(e)) { + if (auto info = dynamic_cast(e)) { set_field(L, "raw", info->GetEntryData()); set_field(L, "key", info->Key()); set_field(L, "value", info->Value()); set_field(L, "class", "info"); } - else if (AssDialogue *dia = dynamic_cast(e)) { + else if (auto dia = dynamic_cast(e)) { set_field(L, "raw", dia->GetEntryData()); set_field(L, "comment", dia->Comment); @@ -176,7 +177,7 @@ namespace Automation4 { set_field(L, "class", "dialogue"); } - else if (AssStyle *sty = dynamic_cast(e)) { + else if (auto sty = dynamic_cast(e)) { set_field(L, "raw", sty->GetEntryData()); set_field(L, "name", sty->name); @@ -308,7 +309,7 @@ namespace Automation4 { // read an indexed AssEntry int idx = lua_tointeger(L, 2); CheckBounds(idx); - AssEntryToLua(L, lines[idx - 1]); + AssEntryToLua(L, idx - 1); return 1; } @@ -350,6 +351,52 @@ namespace Automation4 { return 0; } + void LuaAssFile::InitScriptInfoIfNeeded() + { + if (script_info_copied) return; + size_t i = 0; + for (auto const& info : ass->Info) { + // Just in case an insane user inserted non-info lines into the + // script info section... + while (lines[i]) ++i; + lines_to_delete.emplace_back(agi::util::make_unique(info)); + lines[i++] = lines_to_delete.back().get(); + } + script_info_copied = true; + } + + void LuaAssFile::QueueLineForDeletion(size_t idx) + { + if (!lines[idx] || lines[idx]->Group() == AssEntryGroup::INFO) + InitScriptInfoIfNeeded(); + else + lines_to_delete.emplace_back(lines[idx]); + } + + void LuaAssFile::AssignLine(size_t idx, std::unique_ptr e) + { + auto group = e->Group(); + if (group == AssEntryGroup::INFO) + InitScriptInfoIfNeeded(); + lines[idx] = e.get(); + if (group == AssEntryGroup::INFO) + lines_to_delete.emplace_back(std::move(e)); + else + e.release(); + } + + void LuaAssFile::InsertLine(std::vector &vec, size_t idx, std::unique_ptr e) + { + auto group = e->Group(); + if (group == AssEntryGroup::INFO) + InitScriptInfoIfNeeded(); + vec.insert(vec.begin() + idx, e.get()); + if (group == AssEntryGroup::INFO) + lines_to_delete.emplace_back(std::move(e)); + else + e.release(); + } + void LuaAssFile::ObjectIndexWrite(lua_State *L) { // instead of implementing everything twice, just call the other modification-functions from here @@ -375,11 +422,12 @@ namespace Automation4 { // replace line at index n or delete if (!lua_isnil(L, 3)) { // insert + CheckBounds(n); + auto e = LuaToAssEntry(L); modification_type |= modification_mask(e.get()); - CheckBounds(n); - lines_to_delete.emplace_back(lines[n - 1]); - lines[n - 1] = e.release(); + QueueLineForDeletion(n - 1); + AssignLine(n - 1, std::move(e)); } else { // delete @@ -418,7 +466,7 @@ namespace Automation4 { for (size_t i = 0; i < lines.size(); ++i) { if (id_idx < ids.size() && ids[id_idx] == i) { modification_type |= modification_mask(lines[i]); - lines_to_delete.emplace_back(lines[i]); + QueueLineForDeletion(i); ++id_idx; } else { @@ -440,7 +488,7 @@ namespace Automation4 { for (size_t i = a; i < b; ++i) { modification_type |= modification_mask(lines[i]); - lines_to_delete.emplace_back(lines[i]); + QueueLineForDeletion(i); } lines.erase(lines.begin() + a, lines.begin() + b); @@ -457,24 +505,23 @@ namespace Automation4 { auto e = LuaToAssEntry(L); modification_type |= modification_mask(e.get()); - // Find the appropriate place to put it - auto it = lines.end(); - if (!lines.empty()) { - do { - --it; - } - while (it != lines.begin() && (*it)->Group() != e->Group()); + if (lines.empty()) { + InsertLine(lines, 0, std::move(e)); + continue; } - if (it == lines.end() || (*it)->Group() != e->Group()) { - // The new entry belongs to a group that doesn't exist yet, so - // create it at the end of the file - lines.push_back(e.release()); - } - else { - // Append the entry to the end of the existing group - lines.insert(++it, e.release()); + // Find the appropriate place to put it + auto group = e->Group(); + for (size_t i = lines.size(); i > 0; --i) { + auto cur_group = lines[i - 1] ? lines[i - 1]->Group() : AssEntryGroup::INFO; + if (cur_group == group) { + InsertLine(lines, i, std::move(e)); + break; + } } + + // No lines of this type exist already, so just append it to the end + if (e) InsertLine(lines, lines.size(), std::move(e)); } } @@ -500,7 +547,7 @@ namespace Automation4 { lua_pushvalue(L, i); auto e = LuaToAssEntry(L); modification_type |= modification_mask(e.get()); - new_entries[i - 2] = e.release(); + InsertLine(new_entries, i - 2, std::move(e)); lua_pop(L, 1); } lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end()); @@ -531,7 +578,7 @@ namespace Automation4 { } push_value(L, i + 1); - AssEntryToLua(L, lines[i]); + AssEntryToLua(L, i); return 2; } @@ -596,11 +643,13 @@ namespace Automation4 { std::vector LuaAssFile::ProcessingComplete(wxString const& undo_description) { auto apply_lines = [&](std::vector const& lines) { - ass->Info.clear(); + if (script_info_copied) + ass->Info.clear(); ass->Styles.clear(); ass->Events.clear(); for (auto line : lines) { + if (!line) continue; switch (line->Group()) { case AssEntryGroup::INFO: ass->Info.push_back(*static_cast(line)); break; case AssEntryGroup::STYLE: ass->Styles.push_back(*static_cast(line)); break; @@ -641,11 +690,9 @@ namespace Automation4 { , L(L) , can_modify(can_modify) , can_set_undo(can_set_undo) - , modification_type(0) - , references(2) { for (auto& line : ass->Info) - lines.push_back(&line); + lines.push_back(nullptr); for (auto& line : ass->Styles) lines.push_back(&line); for (auto& line : ass->Events)