aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TODO73
-rw-r--r--src/goto.cpp29
-rw-r--r--src/goto.h39
-rw-r--r--src/help.cpp14
-rw-r--r--src/help.h16
-rw-r--r--src/qregisters.cpp2
-rw-r--r--src/qregisters.h44
-rw-r--r--src/rbtree.cpp6
-rw-r--r--src/rbtree.h138
9 files changed, 218 insertions, 143 deletions
diff --git a/TODO b/TODO
index e37de87..2585efb 100644
--- a/TODO
+++ b/TODO
@@ -25,6 +25,7 @@ Known Bugs:
current macro invocation frame and guarantee ^R == 10 at the beginning
of macros.
* Null-byte in strings not always handled transparently
+ (SciTECO is not 8-bit clean.)
* Saving another user's file will only preserve the user when run as root.
Generally, it is hard to ensure that a) save point files can be created
and b) the file mode and ownership of re-created files can be preserved.
@@ -42,6 +43,15 @@ Known Bugs:
throwing exceptions is unsafe from C-linkage callbacks.
We should abandon the custom allocators and rely on
SciTECO's memory limiting.
+ All C++ allocations could be based on g_malloc/g_slice so we
+ assert on OOM. Instead we improve memory limiting using platform-specific
+ API like malloc_info(). Since the remaining platforms are only obscure
+ ones, the overloaded C++ operators should be sufficient to count the
+ bulk of memory used. Since the necessary sized delete operators are
+ only available beginning with C++14, there'd have to be yet another
+ fallback that stores the memory chunk size at the beginning of the
+ heap object.
+ The UndoToken::get_size() workaround can be removed.
* It is still possible to crash SciTECO using recursive functions,
since they map to the C++ program's call stack.
It is perhaps best to use another ValueStack as a stack of
@@ -52,6 +62,9 @@ Known Bugs:
savepoint restoration.
On Windows we could work around this using
MoveFileEx(file, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
+ This should only happen on abnormal program termination
+ since SciTECO's destructors should clean up everything
+ (test whether this holds true for assertions...).
* Clipboard registers are prone to race conditions if the
contents change between get_size() and get_string() calls.
Also it's a common idiom to query a string and its size,
@@ -59,6 +72,8 @@ Known Bugs:
* Setting window title is broken on ncurses/XTerm.
Perhaps do some XTerm magic here. We can also restore
window titles on exit using XTerm.
+ * Glib (error) messages are not integrated with SciTECO's
+ logging system.
Features:
* Auto-indention could be implemented via context-sensitive
@@ -128,10 +143,12 @@ Features:
* Support loading from stdin (--stdin) and writing to
the current buffer to stdout on exit (--stdout).
This will make it easy to write command line filters,
- This also means we need something like --ed to set the
- ED flags before everything else and --quiet.
+ We will need flags like --8-bit-clean and --quiet with
+ single-letter forms to make it possible to write hash-bang
+ lines like #!...sciteco-wrapper -q8iom
Command line arguments should then also be handled
- differently.
+ differently, passing them in an array or single string
+ register, so they no longer affect the unnamed buffer.
* For third-party macro authors, it is useful to know
the standard library path (e.g. to install new lexers).
There could be a --print-path option, or with the --quiet
@@ -149,18 +166,8 @@ Features:
* EF with buffer id
* ER command: read file into current buffer at dot
* nEW to save a buffer by id
- * use CRTP for RBTrees to avoid unnecessary virtual method calls.
- This means that like the original BSD headers, implementations
- of the rbtree ops will be generated for every usage.
- Since currently, only QRegister tables and goto tables are
- RBTrees, the binary size overhead should be minimal.
- There's another possible optimization: RBTrees define an entry
- field for storing node color. This can be avoided on most
- platforms where G_MEM_ALIGN > 1 by encoding the color in the
- lowest bit of one of the pointers.
- The parent pointer is not required for RBTrees in general,
- but we do use the PREV/NEXT ops to iterate prefixes which requires
- the parent pointer to be maintained.
+ * ^A and stdio in general
+ * autocompletion of long Q-Reg names
* Buffer ids should be "circular", i.e. interpreted modulo the
number of buffers in the ring. This allows "%*" to wrap at the
end of the buffer list.
@@ -176,17 +183,16 @@ Features:
* Command to free Q-Register (remove from table).
e.g. FQ (free Q). :FQ could free by QRegister prefix name for
the common use case of Q-Register subtables and lists.
- * autocompletion of long Q-Reg names
* TECO syntax highlighting
* multiline commandline
* perhaps use Scintilla view as mini buffer.
This means patching Scintilla, so it does not break lines
on new line characters.
* A Scintilla view will allow syntax highlighting
- * command line could highlight dead branches (e.g. gray them out)
* improve GTK interface
* proper command-line widget (best would be a Scintilla view, s.a.)
* speed improvements
+ * command line could highlight dead branches (e.g. gray them out)
* backup files, or even better Journal files:
could write a Macro file for each modified file containing
only basic commands (no loops etc.). it is removed when the file is
@@ -222,6 +228,13 @@ Features:
Each Scintilla view could then be associated with at most
one curses screen.
GTK+ would simply manage a list of windows.
+ * Error handling in SciTECO macros: Allow throwing errors with
+ e.g. [n]EE<description>$ where n is an error code, defaulting
+ to 0 and description is the error string - there could be code-specific
+ defaults. All internal error classes use defined codes.
+ Errors could be catched using a structured try-catch-like construct
+ or by defining an error handling label.
+ Macros may retrieve the code and string of the last error.
Optimizations:
* Add G_UNLIKELY to all error throws.
@@ -243,9 +256,10 @@ Optimizations:
C++11 already allows sized allocators in a class.
Testing shows that this does not speed up things on Linux
and prevents freeing memory on command line termination
- (it would be glibc-specific).
- We should test whether it brings any benefit on Windows
- and implement with a build-time option.
+ (it would be glibc-specific), so it should probably depend on
+ HAVE_MALLOC_TRIM. On all other platforms, it could be assumed
+ to be beneficial or at least not hurt.
+ Best is to test its effect on MSVCRT.
* String::append() could be optimized by ORing a padding
into the realloc() size (e.g. 0xFF).
However, this has not proven effective on Linux/glibc
@@ -253,7 +267,9 @@ Optimizations:
roughly the same size.
Should be tested on Windows, though.
* Scintilla: SETDOCPOINTER resets representations, so we
- have to set SciTECO representations up again often.
+ have to set SciTECO representations up again often when
+ working with Q-Registers. Has been benchmarked to be a
+ major slow-down.
This should be patched in Scintilla.
* commonly used (special) Q-Registers could be cached,
saving the q-reg table lookup
@@ -272,7 +288,20 @@ Optimizations:
use a custom error reporting system similar to GError.
This makes error handling and forwarding explicit as in
plain C code. RTTI can be used to discern different
- exception types.
+ exception types. By adding an error code to the error object
+ (which we will need anyway for supporting error handling in SciTECO
+ macros), we may even avoid RTTI.
+ * The position can be eliminated from UndoTokens by
+ rewriting the UndoStack into a stack of UndoToken lists.
+ Should be a significant memory reduction in interactive mode.
+ * RBTrees define an entry field for storing node color.
+ This can be avoided on most
+ platforms where G_MEM_ALIGN > 1 by encoding the color in the
+ lowest bit of one of the pointers.
+ The parent pointer is not required for RBTrees in general,
+ but we do use the PREV/NEXT ops to iterate prefixes which requires
+ the parent pointer to be maintained.
+ * Add a configure-switch for LTO (--enable-lto).
Documentation:
* Code docs (Doxygen). It's slowly getting better...
diff --git a/src/goto.cpp b/src/goto.cpp
index c5fda9a..01cf0a9 100644
--- a/src/goto.cpp
+++ b/src/goto.cpp
@@ -42,16 +42,15 @@ namespace Goto {
}
gint
-GotoTable::remove(gchar *name)
+GotoTable::remove(const gchar *name)
{
gint existing_pc = -1;
- Label label(name);
- Label *existing = (Label *)RBTree::find(&label);
+ Label *existing = (Label *)RBTreeString::find(name);
if (existing) {
existing_pc = existing->pc;
- RBTree::remove(existing);
+ RBTreeString::remove(existing);
delete existing;
}
@@ -59,35 +58,29 @@ GotoTable::remove(gchar *name)
}
gint
-GotoTable::find(gchar *name)
+GotoTable::find(const gchar *name)
{
- Label label(name);
- Label *existing = (Label *)RBTree::find(&label);
+ Label *existing = (Label *)RBTreeString::find(name);
return existing ? existing->pc : -1;
}
gint
-GotoTable::set(gchar *name, gint pc)
+GotoTable::set(const gchar *name, gint pc)
{
if (pc < 0)
return remove(name);
- Label *label = new Label(name, pc);
- Label *existing;
gint existing_pc = -1;
+ Label *existing = (Label *)RBTreeString::find(name);
- existing = (Label *)RBTree::find(label);
if (existing) {
existing_pc = existing->pc;
g_free(existing->name);
- existing->name = label->name;
- existing->pc = label->pc;
-
- label->name = NULL;
- delete label;
+ existing->name = g_strdup(name);
+ existing->pc = pc;
} else {
- RBTree::insert(label);
+ RBTree::insert(new Label(name, pc));
}
#ifdef DEBUG
@@ -101,7 +94,7 @@ GotoTable::set(gchar *name, gint pc)
void
GotoTable::dump(void)
{
- for (Label *cur = (Label *)RBTree::min();
+ for (Label *cur = (Label *)min();
cur != NULL;
cur = (Label *)cur->next())
g_printf("table[\"%s\"] = %d\n", cur->name, cur->pc);
diff --git a/src/goto.h b/src/goto.h
index 1621acd..4fa5766 100644
--- a/src/goto.h
+++ b/src/goto.h
@@ -30,7 +30,7 @@
namespace SciTECO {
-class GotoTable : public RBTree {
+class GotoTable : private RBTreeString {
class UndoTokenSet : public UndoToken {
GotoTable *table;
@@ -38,7 +38,7 @@ class GotoTable : public RBTree {
gint pc;
public:
- UndoTokenSet(GotoTable *_table, gchar *_name, gint _pc = -1)
+ UndoTokenSet(GotoTable *_table, const gchar *_name, gint _pc = -1)
: table(_table), name(g_strdup(_name)), pc(_pc) {}
~UndoTokenSet()
{
@@ -61,23 +61,12 @@ class GotoTable : public RBTree {
}
};
- class Label : public RBTree::RBEntry {
+ class Label : public RBEntryOwnString {
public:
- gchar *name;
- gint pc;
-
- Label(gchar *_name, gint _pc = -1)
- : name(g_strdup(_name)), pc(_pc) {}
- ~Label()
- {
- g_free(name);
- }
+ gint pc;
- int
- operator <(RBEntry &l2)
- {
- return g_strcmp0(name, ((Label &)l2).name);
- }
+ Label(const gchar *name, gint _pc = -1)
+ : RBEntryOwnString(name), pc(_pc) {}
};
/*
@@ -86,19 +75,25 @@ class GotoTable : public RBTree {
bool must_undo;
public:
- GotoTable(bool _undo = true) : RBTree(), must_undo(_undo) {}
+ GotoTable(bool _undo = true) : must_undo(_undo) {}
- gint remove(gchar *name);
- gint find(gchar *name);
+ gint remove(const gchar *name);
+ gint find(const gchar *name);
- gint set(gchar *name, gint pc);
+ gint set(const gchar *name, gint pc);
inline void
- undo_set(gchar *name, gint pc = -1)
+ undo_set(const gchar *name, gint pc = -1)
{
if (must_undo)
undo.push<UndoTokenSet>(this, name, pc);
}
+ inline void
+ clear(void)
+ {
+ RBTreeString::clear();
+ }
+
#ifdef DEBUG
void dump(void);
#endif
diff --git a/src/help.cpp b/src/help.cpp
index 0205cf1..542a11f 100644
--- a/src/help.cpp
+++ b/src/help.cpp
@@ -51,6 +51,10 @@ HelpIndex::load(void)
GDir *women_dir;
const gchar *basename;
+ if (G_UNLIKELY(min() != NULL))
+ /* already loaded */
+ return;
+
lib_path = QRegisters::globals["$SCITECOPATH"]->get_string();
women_path = g_build_filename(lib_path, "women", NIL);
g_free(lib_path);
@@ -157,10 +161,9 @@ HelpIndex::find(const gchar *name)
*/
gchar *term = String::canonicalize_ctl(name);
- Topic topic(term);
- ret = (Topic *)RBTree::find(&topic);
- g_free(term);
+ ret = (Topic *)RBTreeStringCase::find(term);
+ g_free(term);
return ret;
}
@@ -170,7 +173,7 @@ HelpIndex::set(const gchar *name, const gchar *filename, tecoInt pos)
Topic *topic = new Topic(name, filename, pos);
Topic *existing;
- existing = (Topic *)RBTree::find(topic);
+ existing = (Topic *)RBTree<RBEntryString>::find(topic);
if (existing) {
gchar *basename;
@@ -279,8 +282,7 @@ StateGetHelp::initial(void)
* not depend on the availability of the standard
* library.
*/
- if (G_UNLIKELY(!help_index.min()))
- help_index.load();
+ help_index.load();
}
State *
diff --git a/src/help.h b/src/help.h
index db78979..7ce7ea2 100644
--- a/src/help.h
+++ b/src/help.h
@@ -30,29 +30,21 @@
namespace SciTECO {
-class HelpIndex : public RBTree {
+class HelpIndex : private RBTreeStringCase {
public:
- class Topic : public RBTree::RBEntry {
+ class Topic : public RBEntryOwnString {
public:
- gchar *name;
gchar *filename;
tecoInt pos;
- Topic(const gchar *_name, const gchar *_filename = NULL, tecoInt _pos = 0)
- : name(g_strdup(_name)),
+ Topic(const gchar *name, const gchar *_filename = NULL, tecoInt _pos = 0)
+ : RBEntryOwnString(name),
filename(_filename ? g_strdup(_filename) : NULL),
pos(_pos) {}
~Topic()
{
- g_free(name);
g_free(filename);
}
-
- int
- operator <(RBEntry &l2)
- {
- return g_ascii_strcasecmp(name, ((Topic &)l2).name);
- }
};
void load(void);
diff --git a/src/qregisters.cpp b/src/qregisters.cpp
index c4c164f..6ca7990 100644
--- a/src/qregisters.cpp
+++ b/src/qregisters.cpp
@@ -696,7 +696,7 @@ QRegisterClipboard::undo_exchange_string(QRegisterData &reg)
reg.undo_set_string();
}
-QRegisterTable::QRegisterTable(bool _undo) : RBTree(), must_undo(_undo)
+QRegisterTable::QRegisterTable(bool _undo) : must_undo(_undo)
{
/* general purpose registers */
for (gchar q = 'A'; q <= 'Z'; q++)
diff --git a/src/qregisters.h b/src/qregisters.h
index 22fa93d..1ea47ee 100644
--- a/src/qregisters.h
+++ b/src/qregisters.h
@@ -134,30 +134,17 @@ public:
friend class QRegisterStack;
};
-class QRegister : public RBTree::RBEntry, public QRegisterData {
+class QRegister : public RBTreeString::RBEntryOwnString, public QRegisterData {
protected:
/**
* The default constructor for subclasses.
* This leaves the name uninitialized.
*/
- QRegister(void) : name(NULL) {}
+ QRegister() {}
public:
- gchar *name;
-
- QRegister(const gchar *_name)
- : name(g_strdup(_name)) {}
- virtual
- ~QRegister()
- {
- g_free(name);
- }
-
- int
- operator <(RBEntry &entry)
- {
- return g_strcmp0(name, ((QRegister &)entry).name);
- }
+ QRegister(const gchar *name)
+ : RBTreeString::RBEntryOwnString(name) {}
virtual void edit(void);
virtual void undo_edit(void);
@@ -300,7 +287,7 @@ public:
void undo_exchange_string(QRegisterData &reg);
};
-class QRegisterTable : private RBTree {
+class QRegisterTable : private RBTreeString {
class UndoTokenRemove : public UndoTokenWithSize<UndoTokenRemove> {
QRegisterTable *table;
QRegister *reg;
@@ -332,11 +319,7 @@ public:
insert(QRegister *reg)
{
reg->must_undo = must_undo;
- /* FIXME: Returns already existing regs with the same name.
- This could be used to optimize commands that initialize
- a register if it does not yet exist (saves one table
- lookup): */
- RBTree::insert(reg);
+ RBTreeString::insert(reg);
return reg;
}
inline QRegister *
@@ -352,30 +335,33 @@ public:
}
inline QRegister *
+ find(const gchar *name)
+ {
+ return (QRegister *)RBTreeString::find(name);
+ }
+ inline QRegister *
operator [](const gchar *name)
{
- QRegister reg(name);
- return (QRegister *)find(&reg);
+ return find(name);
}
inline QRegister *
operator [](gchar chr)
{
gchar buf[] = {chr, '\0'};
- return operator [](buf);
+ return find(buf);
}
inline QRegister *
nfind(const gchar *name)
{
- QRegister reg(name);
- return (QRegister *)RBTree::nfind(&reg);
+ return (QRegister *)RBTreeString::nfind(name);
}
void edit(QRegister *reg);
inline QRegister *
edit(const gchar *name)
{
- QRegister *reg = operator [](name);
+ QRegister *reg = find(name);
if (!reg)
return NULL;
diff --git a/src/rbtree.cpp b/src/rbtree.cpp
index e64de96..f1d870d 100644
--- a/src/rbtree.cpp
+++ b/src/rbtree.cpp
@@ -19,12 +19,14 @@
#include "config.h"
#endif
-#include <bsd/sys/tree.h>
+#include <glib.h>
+#include <glib/gprintf.h>
#include "rbtree.h"
namespace SciTECO {
-RB_GENERATE(RBTree::Tree, RBTree::RBEntry, nodes, RBTree::compare_entries);
+template class RBTreeStringT<strcmp, strncmp>;
+template class RBTreeStringT<g_ascii_strcasecmp, g_ascii_strncasecmp>;
} /* namespace SciTECO */
diff --git a/src/rbtree.h b/src/rbtree.h
index bc836bd..4a4bbdb 100644
--- a/src/rbtree.h
+++ b/src/rbtree.h
@@ -18,30 +18,22 @@
#ifndef __RBTREE_H
#define __RBTREE_H
+#include <string.h>
+
#include <bsd/sys/tree.h>
#include <glib.h>
-
-#include "undo.h"
+#include <glib/gprintf.h>
namespace SciTECO {
+template <class RBEntryType>
class RBTree {
public:
- class RBEntry;
-
-private:
- RB_HEAD(Tree, RBEntry) head;
-
- RB_PROTOTYPE_INTERNAL(Tree, RBEntry, nodes, /* unused */, static);
-
-public:
class RBEntry {
public:
RB_ENTRY(RBEntry) nodes;
- virtual ~RBEntry() {}
-
inline RBEntry *
next(void)
{
@@ -52,17 +44,23 @@ public:
{
return RBTree::Tree_RB_PREV(this);
}
-
- virtual int operator <(RBEntry &entry) = 0;
};
private:
+ RB_HEAD(Tree, RBEntry) head;
+
static inline int
compare_entries(RBEntry *e1, RBEntry *e2)
{
- return *e1 < *e2;
+ return ((RBEntryType *)e1)->compare(*(RBEntryType *)e2);
}
+ /*
+ * All generated functions are plain-C, so they can be
+ * static methods.
+ */
+ RB_GENERATE_INTERNAL(Tree, RBEntry, nodes, compare_entries, static);
+
public:
RBTree()
{
@@ -74,42 +72,47 @@ public:
clear();
}
- inline RBEntry *
- insert(RBEntry *entry)
+ inline RBEntryType *
+ insert(RBEntryType *entry)
{
RB_INSERT(Tree, &head, entry);
return entry;
}
- inline RBEntry *
- remove(RBEntry *entry)
+ inline RBEntryType *
+ remove(RBEntryType *entry)
+ {
+ return (RBEntryType *)RB_REMOVE(Tree, &head, entry);
+ }
+ inline RBEntryType *
+ find(RBEntryType *entry)
{
- return RB_REMOVE(Tree, &head, entry);
+ return (RBEntryType *)RB_FIND(Tree, &head, entry);
}
- inline RBEntry *
- find(RBEntry *entry)
+ inline RBEntryType *
+ operator [](RBEntryType *entry)
{
- return RB_FIND(Tree, &head, entry);
+ return find(entry);
}
- inline RBEntry *
- nfind(RBEntry *entry)
+ inline RBEntryType *
+ nfind(RBEntryType *entry)
{
- return RB_NFIND(Tree, &head, entry);
+ return (RBEntryType *)RB_NFIND(Tree, &head, entry);
}
- inline RBEntry *
+ inline RBEntryType *
min(void)
{
- return RB_MIN(Tree, &head);
+ return (RBEntryType *)RB_MIN(Tree, &head);
}
- inline RBEntry *
+ inline RBEntryType *
max(void)
{
- return RB_MAX(Tree, &head);
+ return (RBEntryType *)RB_MAX(Tree, &head);
}
inline void
clear(void)
{
- RBEntry *cur;
+ RBEntryType *cur;
while ((cur = min())) {
remove(cur);
@@ -118,6 +121,79 @@ public:
}
};
+typedef gint (*StringCmpFunc)(const gchar *str1, const gchar *str2);
+typedef gint (*StringNCmpFunc)(const gchar *str1, const gchar *str2, gsize n);
+
+template <StringCmpFunc StringCmp>
+class RBEntryStringT : public RBTree<RBEntryStringT<StringCmp>>::RBEntry {
+public:
+ /*
+ * It is convenient to be able to access the string
+ * key with various attribute names.
+ */
+ union {
+ gchar *key;
+ gchar *name;
+ };
+
+ RBEntryStringT(gchar *_key) : key(_key) {}
+
+ virtual ~RBEntryStringT() {}
+
+ inline gint
+ compare(RBEntryStringT &other)
+ {
+ return StringCmp(key, other.key);
+ }
+};
+
+template <StringCmpFunc StringCmp, StringNCmpFunc StringNCmp>
+class RBTreeStringT : public RBTree<RBEntryStringT<StringCmp>> {
+public:
+ typedef RBEntryStringT<StringCmp> RBEntryString;
+
+ class RBEntryOwnString : public RBEntryString {
+ public:
+ RBEntryOwnString(const gchar *key = NULL)
+ : RBEntryString(key ? g_strdup(key) : NULL) {}
+
+ ~RBEntryOwnString()
+ {
+ g_free(RBEntryString::key);
+ }
+ };
+
+ inline RBEntryString *
+ find(const gchar *str)
+ {
+ RBEntryString entry((gchar *)str);
+ return RBTree<RBEntryString>::find(&entry);
+ }
+ inline RBEntryString *
+ operator [](const gchar *name)
+ {
+ return find(name);
+ }
+
+ inline RBEntryString *
+ nfind(const gchar *str)
+ {
+ RBEntryString entry((gchar *)str);
+ return RBTree<RBEntryString>::nfind(&entry);
+ }
+};
+
+typedef RBTreeStringT<strcmp, strncmp> RBTreeString;
+typedef RBTreeStringT<g_ascii_strcasecmp, g_ascii_strncasecmp> RBTreeStringCase;
+
+/*
+ * Only these two instantiations of RBTreeStringT are ever used,
+ * so it is more efficient to explicitly instantiate them.
+ * NOTE: The insane rules of C++ prevent using the typedefs here...
+ */
+extern template class RBTreeStringT<strcmp, strncmp>;
+extern template class RBTreeStringT<g_ascii_strcasecmp, g_ascii_strncasecmp>;
+
} /* namespace SciTECO */
#endif