/*
* Copyright (C) 2012-2015 Robin Haberkorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __UNDO_H
#define __UNDO_H
#include
#include
#include
#include
#ifdef DEBUG
#include "parser.h"
#endif
namespace SciTECO {
/**
* Default undo stack memory limit (500mb).
*/
#define UNDO_MEMORY_LIMIT_DEFAULT (500*1024*1024)
class UndoToken {
public:
SLIST_ENTRY(UndoToken) tokens;
/**
* Command-line character position (program counter)
* corresponding to this token.
*
* @todo This wastes memory in macro calls and loops
* because all undo tokens will have the same
* value. It may be better to redesign the undo
* stack data structure - as a list/array pointing
* to undo stacks per character.
*/
gint pc;
virtual ~UndoToken() {}
virtual void run(void) = 0;
/**
* Return approximated size of this object.
* If possible it should take all heap objects
* into account that are memory-managed by the undo
* token.
* For a simple implementation, you may derive
* from UndoTokenWithSize.
*/
virtual gsize get_size(void) const = 0;
};
/**
* UndoToken base class employing the CRTP idiom.
* Deriving this class adds a size approximation based
* on the shallow size of the template parameter.
*/
template
class UndoTokenWithSize : public UndoToken {
public:
gsize
get_size(void) const
{
return sizeof(UndoTokenImpl);
}
};
template
class UndoTokenVariable : public UndoTokenWithSize< UndoTokenVariable > {
Type *ptr;
Type value;
public:
UndoTokenVariable(Type &variable, Type _value)
: ptr(&variable), value(_value) {}
void
run(void)
{
#ifdef DEBUG
if ((State **)ptr == &States::current)
g_printf("undo state -> %p\n", (void *)value);
#endif
*ptr = value;
}
};
class UndoTokenString : public UndoToken {
gchar **ptr;
gchar *str;
public:
UndoTokenString(gchar *&variable, gchar *_str)
: ptr(&variable)
{
str = _str ? g_strdup(_str) : NULL;
}
~UndoTokenString()
{
g_free(str);
}
void
run(void)
{
g_free(*ptr);
*ptr = str;
str = NULL;
}
gsize
get_size(void) const
{
return str ? sizeof(*this) + strlen(str) + 1
: sizeof(*this);
}
};
template
class UndoTokenObject : public UndoToken {
Type **ptr;
Type *obj;
public:
UndoTokenObject(Type *&variable, Type *_obj)
: ptr(&variable), obj(_obj) {}
~UndoTokenObject()
{
delete obj;
}
void
run(void)
{
delete *ptr;
*ptr = obj;
obj = NULL;
}
gsize
get_size(void) const
{
return obj ? sizeof(*this) + sizeof(*obj)
: sizeof(*this);
}
};
extern class UndoStack {
SLIST_HEAD(Head, UndoToken) head;
/**
* Current approx. memory usage of all
* undo tokens in the stack.
* It is only up to date if memory limiting
* is enabled.
*/
gsize memory_usage;
public:
bool enabled;
/**
* Undo stack memory limit in bytes.
* 0 means no limiting.
*/
gsize memory_limit;
UndoStack(bool _enabled = false)
: memory_usage(0), enabled(_enabled),
memory_limit(UNDO_MEMORY_LIMIT_DEFAULT)
{
SLIST_INIT(&head);
}
~UndoStack();
void set_memory_limit(gsize new_limit = 0);
void push(UndoToken *token);
template
inline Type &
push_var(Type &variable, Type value)
{
push(new UndoTokenVariable(variable, value));
return variable;
}
template
inline Type &
push_var(Type &variable)
{
return push_var(variable, variable);
}
inline gchar *&
push_str(gchar *&variable, gchar *str)
{
push(new UndoTokenString(variable, str));
return variable;
}
inline gchar *&
push_str(gchar *&variable)
{
return push_str(variable, variable);
}
template
inline Type *&
push_obj(Type *&variable, Type *obj)
{
push(new UndoTokenObject(variable, obj));
return variable;
}
template
inline Type *&
push_obj(Type *&variable)
{
return push_obj(variable, variable);
}
void pop(gint pc);
void clear(void);
} undo;
} /* namespace SciTECO */
#endif