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);
/// 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<AssEntry*> lines;
bool script_info_copied = false;
/// Commits to apply once processing completes successfully
std::deque<PendingCommit> pending_commits;
/// Lines to delete once processing complete successfully
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);
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<AssEntry> LuaToAssEntry(lua_State *L);

View File

@ -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 <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/range/adaptor/indirected.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <cassert>
#include <memory>
@ -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<AssInfo*>(e)) {
if (auto info = dynamic_cast<const AssInfo*>(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<AssDialogue*>(e)) {
else if (auto dia = dynamic_cast<const AssDialogue*>(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<AssStyle*>(e)) {
else if (auto sty = dynamic_cast<const AssStyle*>(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<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)
{
// 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<AssEntry *> LuaAssFile::ProcessingComplete(wxString const& undo_description)
{
auto apply_lines = [&](std::vector<AssEntry *> 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<AssInfo *>(line)); break;
case AssEntryGroup::STYLE: ass->Styles.push_back(*static_cast<AssStyle *>(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)