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.
This commit is contained in:
Thomas Goyne 2014-03-08 19:14:22 -08:00
parent f6463f0fd4
commit 8ee3095506
2 changed files with 101 additions and 39 deletions

View File

@ -73,19 +73,34 @@ namespace Automation4 {
void CheckBounds(int idx); void CheckBounds(int idx);
/// How ass file been modified by the script since the last commit /// 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 /// Reference count used to avoid deleting this until both lua and the
/// calling C++ code are done with it /// 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 /// Set of subtitle lines being modified; initially a shallow copy of ass->Line
std::vector<AssEntry*> lines; std::vector<AssEntry*> lines;
bool script_info_copied = false;
/// Commits to apply once processing completes successfully /// Commits to apply once processing completes successfully
std::deque<PendingCommit> pending_commits; std::deque<PendingCommit> pending_commits;
/// Lines to delete once processing complete successfully /// Lines to delete once processing complete successfully
std::vector<std::unique_ptr<AssEntry>> lines_to_delete; std::vector<std::unique_ptr<AssEntry>> 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<AssEntry> e);
void InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e);
int ObjectIndexRead(lua_State *L); int ObjectIndexRead(lua_State *L);
void ObjectIndexWrite(lua_State *L); void ObjectIndexWrite(lua_State *L);
int ObjectGetLen(lua_State *L); int ObjectGetLen(lua_State *L);
@ -107,7 +122,7 @@ namespace Automation4 {
static LuaAssFile *GetObjPointer(lua_State *L, int idx); static LuaAssFile *GetObjPointer(lua_State *L, int idx);
/// makes a Lua representation of AssEntry and places on the top of the stack /// 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 /// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L); static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L);

View File

@ -37,7 +37,6 @@
#include "auto4_lua.h" #include "auto4_lua.h"
#include "auto4_lua_utils.h" #include "auto4_lua_utils.h"
#include "ass_attachment.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_info.h" #include "ass_info.h"
#include "ass_file.h" #include "ass_file.h"
@ -50,8 +49,6 @@
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <boost/range/adaptor/indirected.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <cassert> #include <cassert>
#include <memory> #include <memory>
@ -118,8 +115,8 @@ namespace {
int modification_mask(AssEntry *e) 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::DIALOGUE: return AssFile::COMMIT_DIAG_ADDREM;
case AssEntryGroup::STYLE: return AssFile::COMMIT_STYLES; case AssEntryGroup::STYLE: return AssFile::COMMIT_STYLES;
default: return AssFile::COMMIT_SCRIPTINFO; default: return AssFile::COMMIT_SCRIPTINFO;
@ -142,19 +139,23 @@ namespace Automation4 {
luaL_error(L, "Requested out-of-range line from subtitle file: %d", idx); 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); lua_newtable(L);
const AssEntry *e = lines[idx];
if (!e)
e = &ass->Info[idx];
set_field(L, "section", e->GroupHeader()); set_field(L, "section", e->GroupHeader());
if (AssInfo *info = dynamic_cast<AssInfo*>(e)) { if (auto info = dynamic_cast<const AssInfo*>(e)) {
set_field(L, "raw", info->GetEntryData()); set_field(L, "raw", info->GetEntryData());
set_field(L, "key", info->Key()); set_field(L, "key", info->Key());
set_field(L, "value", info->Value()); set_field(L, "value", info->Value());
set_field(L, "class", "info"); set_field(L, "class", "info");
} }
else if (AssDialogue *dia = dynamic_cast<AssDialogue*>(e)) { else if (auto dia = dynamic_cast<const AssDialogue*>(e)) {
set_field(L, "raw", dia->GetEntryData()); set_field(L, "raw", dia->GetEntryData());
set_field(L, "comment", dia->Comment); set_field(L, "comment", dia->Comment);
@ -176,7 +177,7 @@ namespace Automation4 {
set_field(L, "class", "dialogue"); set_field(L, "class", "dialogue");
} }
else if (AssStyle *sty = dynamic_cast<AssStyle*>(e)) { else if (auto sty = dynamic_cast<const AssStyle*>(e)) {
set_field(L, "raw", sty->GetEntryData()); set_field(L, "raw", sty->GetEntryData());
set_field(L, "name", sty->name); set_field(L, "name", sty->name);
@ -308,7 +309,7 @@ namespace Automation4 {
// read an indexed AssEntry // read an indexed AssEntry
int idx = lua_tointeger(L, 2); int idx = lua_tointeger(L, 2);
CheckBounds(idx); CheckBounds(idx);
AssEntryToLua(L, lines[idx - 1]); AssEntryToLua(L, idx - 1);
return 1; return 1;
} }
@ -350,6 +351,52 @@ namespace Automation4 {
return 0; 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<AssInfo>(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<AssEntry> 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<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> 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) void LuaAssFile::ObjectIndexWrite(lua_State *L)
{ {
// instead of implementing everything twice, just call the other modification-functions from here // 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 // replace line at index n or delete
if (!lua_isnil(L, 3)) { if (!lua_isnil(L, 3)) {
// insert // insert
CheckBounds(n);
auto e = LuaToAssEntry(L); auto e = LuaToAssEntry(L);
modification_type |= modification_mask(e.get()); modification_type |= modification_mask(e.get());
CheckBounds(n); QueueLineForDeletion(n - 1);
lines_to_delete.emplace_back(lines[n - 1]); AssignLine(n - 1, std::move(e));
lines[n - 1] = e.release();
} }
else { else {
// delete // delete
@ -418,7 +466,7 @@ namespace Automation4 {
for (size_t i = 0; i < lines.size(); ++i) { for (size_t i = 0; i < lines.size(); ++i) {
if (id_idx < ids.size() && ids[id_idx] == i) { if (id_idx < ids.size() && ids[id_idx] == i) {
modification_type |= modification_mask(lines[i]); modification_type |= modification_mask(lines[i]);
lines_to_delete.emplace_back(lines[i]); QueueLineForDeletion(i);
++id_idx; ++id_idx;
} }
else { else {
@ -440,7 +488,7 @@ namespace Automation4 {
for (size_t i = a; i < b; ++i) { for (size_t i = a; i < b; ++i) {
modification_type |= modification_mask(lines[i]); modification_type |= modification_mask(lines[i]);
lines_to_delete.emplace_back(lines[i]); QueueLineForDeletion(i);
} }
lines.erase(lines.begin() + a, lines.begin() + b); lines.erase(lines.begin() + a, lines.begin() + b);
@ -457,24 +505,23 @@ namespace Automation4 {
auto e = LuaToAssEntry(L); auto e = LuaToAssEntry(L);
modification_type |= modification_mask(e.get()); modification_type |= modification_mask(e.get());
// Find the appropriate place to put it if (lines.empty()) {
auto it = lines.end(); InsertLine(lines, 0, std::move(e));
if (!lines.empty()) { continue;
do {
--it;
}
while (it != lines.begin() && (*it)->Group() != e->Group());
} }
if (it == lines.end() || (*it)->Group() != e->Group()) { // Find the appropriate place to put it
// The new entry belongs to a group that doesn't exist yet, so auto group = e->Group();
// create it at the end of the file for (size_t i = lines.size(); i > 0; --i) {
lines.push_back(e.release()); auto cur_group = lines[i - 1] ? lines[i - 1]->Group() : AssEntryGroup::INFO;
} if (cur_group == group) {
else { InsertLine(lines, i, std::move(e));
// Append the entry to the end of the existing group break;
lines.insert(++it, e.release()); }
} }
// 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); lua_pushvalue(L, i);
auto e = LuaToAssEntry(L); auto e = LuaToAssEntry(L);
modification_type |= modification_mask(e.get()); modification_type |= modification_mask(e.get());
new_entries[i - 2] = e.release(); InsertLine(new_entries, i - 2, std::move(e));
lua_pop(L, 1); lua_pop(L, 1);
} }
lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end()); lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end());
@ -531,7 +578,7 @@ namespace Automation4 {
} }
push_value(L, i + 1); push_value(L, i + 1);
AssEntryToLua(L, lines[i]); AssEntryToLua(L, i);
return 2; return 2;
} }
@ -596,11 +643,13 @@ namespace Automation4 {
std::vector<AssEntry *> LuaAssFile::ProcessingComplete(wxString const& undo_description) std::vector<AssEntry *> LuaAssFile::ProcessingComplete(wxString const& undo_description)
{ {
auto apply_lines = [&](std::vector<AssEntry *> const& lines) { auto apply_lines = [&](std::vector<AssEntry *> const& lines) {
ass->Info.clear(); if (script_info_copied)
ass->Info.clear();
ass->Styles.clear(); ass->Styles.clear();
ass->Events.clear(); ass->Events.clear();
for (auto line : lines) { for (auto line : lines) {
if (!line) continue;
switch (line->Group()) { switch (line->Group()) {
case AssEntryGroup::INFO: ass->Info.push_back(*static_cast<AssInfo *>(line)); break; case AssEntryGroup::INFO: ass->Info.push_back(*static_cast<AssInfo *>(line)); break;
case AssEntryGroup::STYLE: ass->Styles.push_back(*static_cast<AssStyle *>(line)); break; case AssEntryGroup::STYLE: ass->Styles.push_back(*static_cast<AssStyle *>(line)); break;
@ -641,11 +690,9 @@ namespace Automation4 {
, L(L) , L(L)
, can_modify(can_modify) , can_modify(can_modify)
, can_set_undo(can_set_undo) , can_set_undo(can_set_undo)
, modification_type(0)
, references(2)
{ {
for (auto& line : ass->Info) for (auto& line : ass->Info)
lines.push_back(&line); lines.push_back(nullptr);
for (auto& line : ass->Styles) for (auto& line : ass->Styles)
lines.push_back(&line); lines.push_back(&line);
for (auto& line : ass->Events) for (auto& line : ass->Events)