aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/undo.h
blob: 9fc5925ba25205e37537b784e44c339e160e6084 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#ifndef __UNDO_H
#define __UNDO_H

#include <string.h>

#include <bsd/sys/queue.h>

#include <glib.h>
#include <glib/gprintf.h>

#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 UndoTokenImpl>
class UndoTokenWithSize : public UndoToken {
public:
	gsize
	get_size(void) const
	{
		return sizeof(UndoTokenImpl);
	}
};

template <typename Type>
class UndoTokenVariable : public UndoTokenWithSize< UndoTokenVariable<Type> > {
	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 Type>
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 <typename Type>
	inline Type &
	push_var(Type &variable, Type value)
	{
		push(new UndoTokenVariable<Type>(variable, value));
		return variable;
	}

	template <typename Type>
	inline Type &
	push_var(Type &variable)
	{
		return push_var<Type>(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 <class Type>
	inline Type *&
	push_obj(Type *&variable, Type *obj)
	{
		push(new UndoTokenObject<Type>(variable, obj));
		return variable;
	}

	template <class Type>
	inline Type *&
	push_obj(Type *&variable)
	{
		return push_obj<Type>(variable, variable);
	}

	void pop(gint pc);

	void clear(void);
} undo;

} /* namespace SciTECO */

#endif