gap_buffer 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +38 -0
- data/Rakefile +71 -0
- data/bench/gap_buffer.rb +40 -0
- data/ext/gap_buffer/extconf.rb +62 -0
- data/ext/gap_buffer/gap_buffer.cpp +480 -0
- data/ext/gap_buffer/gap_buffer.h +159 -0
- data/ext/gap_buffer/rubymain.cpp +191 -0
- data/gap_buffer.gemspec +14 -0
- data/test/test.rb +3 -0
- data/test/test_gap_buffer.rb +140 -0
- metadata +75 -0
data/README
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Implementation of a Gap Buffer for Ruby MRI.
|
2
|
+
(c) 2010 Lourens Naudé (methodmissing), based off work from Hsin Tsao <stsao@lazyhacker.com>
|
3
|
+
|
4
|
+
http://github.com/methodmissing/gap_buffer
|
5
|
+
|
6
|
+
How Gap Buffers work :
|
7
|
+
|
8
|
+
http://en.wikipedia.org/wiki/Gap_buffer
|
9
|
+
http://www.lazyhacker.com/gapbuffer/gapbuffer.htm
|
10
|
+
|
11
|
+
This library works with Ruby 1.8 and 1.9 and exposes the following API :
|
12
|
+
|
13
|
+
gb = GapBuffer.new
|
14
|
+
gb.size #=> 20
|
15
|
+
gb.offset #=> 9
|
16
|
+
gb << "test"
|
17
|
+
gb.print #=> test_______________
|
18
|
+
gb.insert('b')
|
19
|
+
gb.offset #=> 5
|
20
|
+
gb.print #=> testb______________
|
21
|
+
gb.insert_at(6, 'u')
|
22
|
+
gb.print #=> testbu_____________
|
23
|
+
gb.previous #=> b
|
24
|
+
gb.next #=> u
|
25
|
+
gb.offset = 7
|
26
|
+
gb.put('f')
|
27
|
+
gb.print #=> testbuf____________
|
28
|
+
|
29
|
+
To run the test suite:
|
30
|
+
|
31
|
+
rake
|
32
|
+
|
33
|
+
Todo:
|
34
|
+
|
35
|
+
Better 1.9 compat
|
36
|
+
Look into moving off the C++ implementation
|
37
|
+
|
38
|
+
Work in progress, thanks for watching!
|
data/Rakefile
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/clean'
|
4
|
+
$:.unshift(File.expand_path('lib'))
|
5
|
+
GAP_BUFFER_ROOT = 'ext/gap_buffer'
|
6
|
+
|
7
|
+
desc 'Default: test'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run gap_buffer tests.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs = [GAP_BUFFER_ROOT]
|
13
|
+
t.pattern = 'test/test_*.rb'
|
14
|
+
t.ruby_opts << '-rtest'
|
15
|
+
t.libs << 'test'
|
16
|
+
t.warning = true
|
17
|
+
t.verbose = true
|
18
|
+
end
|
19
|
+
task :test => :build
|
20
|
+
|
21
|
+
namespace :build do
|
22
|
+
file "#{GAP_BUFFER_ROOT}/rubymain.cpp"
|
23
|
+
file "#{GAP_BUFFER_ROOT}/extconf.rb"
|
24
|
+
file "#{GAP_BUFFER_ROOT}/Makefile" => %W(#{GAP_BUFFER_ROOT}/rubymain.cpp #{GAP_BUFFER_ROOT}/extconf.rb) do
|
25
|
+
Dir.chdir(GAP_BUFFER_ROOT) do
|
26
|
+
ruby 'extconf.rb'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "generate makefile"
|
31
|
+
task :makefile => %W(#{GAP_BUFFER_ROOT}/Makefile #{GAP_BUFFER_ROOT}/rubymain.cpp)
|
32
|
+
|
33
|
+
dlext = Config::CONFIG['DLEXT']
|
34
|
+
file "#{GAP_BUFFER_ROOT}/gap_buffer.#{dlext}" => %W(#{GAP_BUFFER_ROOT}/Makefile #{GAP_BUFFER_ROOT}/rubymain.cpp) do
|
35
|
+
Dir.chdir(GAP_BUFFER_ROOT) do
|
36
|
+
sh 'make' # TODO - is there a config for which make somewhere?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "compile gap buffer extension"
|
41
|
+
task :compile => "#{GAP_BUFFER_ROOT}/gap_buffer.#{dlext}"
|
42
|
+
|
43
|
+
task :clean do
|
44
|
+
Dir.chdir(GAP_BUFFER_ROOT) do
|
45
|
+
sh 'make clean'
|
46
|
+
end if File.exists?("#{GAP_BUFFER_ROOT}/Makefile")
|
47
|
+
end
|
48
|
+
|
49
|
+
CLEAN.include("#{GAP_BUFFER_ROOT}/Makefile")
|
50
|
+
CLEAN.include("#{GAP_BUFFER_ROOT}/gap_buffer.#{dlext}")
|
51
|
+
end
|
52
|
+
|
53
|
+
task :clean => %w(build:clean)
|
54
|
+
|
55
|
+
desc "compile"
|
56
|
+
task :build => %w(build:compile)
|
57
|
+
|
58
|
+
task :install do |t|
|
59
|
+
Dir.chdir(GAP_BUFFER_ROOT) do
|
60
|
+
sh 'sudo make install'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "clean build install"
|
65
|
+
task :setup => %w(clean build install)
|
66
|
+
|
67
|
+
desc 'Run benchmarks'
|
68
|
+
task :bench do
|
69
|
+
ruby "bench/gap_buffer.rb"
|
70
|
+
end
|
71
|
+
task :bench => :build
|
data/bench/gap_buffer.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift "."
|
2
|
+
require 'benchmark'
|
3
|
+
require File.dirname(__FILE__) + '/../ext/gap_buffer/gap_buffer'
|
4
|
+
|
5
|
+
TESTS = 1000
|
6
|
+
ITEMS = (0..TESTS).to_a
|
7
|
+
|
8
|
+
def prepare
|
9
|
+
string = ITEMS.join
|
10
|
+
gb = GapBuffer.new(TESTS)
|
11
|
+
gb << ITEMS.join
|
12
|
+
[string, gb]
|
13
|
+
end
|
14
|
+
|
15
|
+
Benchmark.bmbm do |results|
|
16
|
+
results.report("[String] random insert") do
|
17
|
+
string, gb = prepare
|
18
|
+
TESTS.times{ r = rand(TESTS); string[r] = r.to_s }
|
19
|
+
end
|
20
|
+
results.report("[GapBuffer] random insert") do
|
21
|
+
string, gb = prepare
|
22
|
+
TESTS.times{ r = rand(TESTS); gb.insert_at(r, r.to_s); }
|
23
|
+
end
|
24
|
+
results.report("[String] insert") do
|
25
|
+
string, gb = prepare
|
26
|
+
TESTS.times{|i| string[i] = i.to_s }
|
27
|
+
end
|
28
|
+
results.report("[GapBuffer] insert") do
|
29
|
+
string, gb = prepare
|
30
|
+
TESTS.times{|i| gb.insert_at(i, i.to_s); }
|
31
|
+
end
|
32
|
+
results.report("[String] random remove") do
|
33
|
+
string, gb = prepare
|
34
|
+
TESTS.times{ r = rand(TESTS); string.delete!(r.to_s) }
|
35
|
+
end
|
36
|
+
results.report("[GapBuffer] random remove") do
|
37
|
+
string, gb = prepare
|
38
|
+
TESTS.times{ r = rand(TESTS); gb.delete(r) }
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
dir_config('gap_buffer')
|
4
|
+
have_func('rb_thread_blocking_region')
|
5
|
+
|
6
|
+
# Shamelessly leached from Eventmachine (https://github.com/eventmachine/eventmachine)
|
7
|
+
|
8
|
+
case RUBY_PLATFORM
|
9
|
+
when /mswin32/, /mingw32/, /bccwin32/
|
10
|
+
check_heads(%w[windows.h winsock.h], true)
|
11
|
+
check_libs(%w[kernel32 rpcrt4 gdi32], true)
|
12
|
+
|
13
|
+
if GNU_CHAIN
|
14
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++"
|
15
|
+
else
|
16
|
+
$defs.push "-EHs"
|
17
|
+
$defs.push "-GR"
|
18
|
+
end
|
19
|
+
|
20
|
+
when /solaris/
|
21
|
+
add_define 'OS_SOLARIS8'
|
22
|
+
check_libs(%w[nsl socket], true)
|
23
|
+
|
24
|
+
if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler
|
25
|
+
# SUN CHAIN
|
26
|
+
add_define 'CC_SUNWspro'
|
27
|
+
$preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
|
28
|
+
$CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
|
29
|
+
CONFIG['CCDLFLAGS'] = "-KPIC"
|
30
|
+
CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
|
31
|
+
else
|
32
|
+
# GNU CHAIN
|
33
|
+
# on Unix we need a g++ link, not gcc.
|
34
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
35
|
+
end
|
36
|
+
|
37
|
+
when /openbsd/
|
38
|
+
# OpenBSD branch contributed by Guillaume Sellier.
|
39
|
+
|
40
|
+
# on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
|
41
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
|
42
|
+
CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
|
43
|
+
|
44
|
+
when /darwin/
|
45
|
+
# on Unix we need a g++ link, not gcc.
|
46
|
+
# Ff line contributed by Daniel Harple.
|
47
|
+
CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
|
48
|
+
|
49
|
+
when /linux/
|
50
|
+
# on Unix we need a g++ link, not gcc.
|
51
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
52
|
+
|
53
|
+
when /aix/
|
54
|
+
# on Unix we need a g++ link, not gcc.
|
55
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G"
|
56
|
+
|
57
|
+
else
|
58
|
+
# on Unix we need a g++ link, not gcc.
|
59
|
+
CONFIG['LDSHARED'] = "$(CXX) -shared"
|
60
|
+
end
|
61
|
+
|
62
|
+
create_makefile('gap_buffer')
|
@@ -0,0 +1,480 @@
|
|
1
|
+
/*
|
2
|
+
* gap_buffer.cpp
|
3
|
+
*
|
4
|
+
* Author: Hsin Tsao (stsao@lazyhacker.com)
|
5
|
+
* Version: 1.0 (June 12, 2003)
|
6
|
+
*
|
7
|
+
* This provides the implementation to the GapBuffer class defined
|
8
|
+
* in text_buffer.h.
|
9
|
+
*
|
10
|
+
* Portions of this work derived from Joseph Allen's usenet
|
11
|
+
* postings on comp.editor that was released to the public
|
12
|
+
* domain.
|
13
|
+
*
|
14
|
+
*
|
15
|
+
* There are no restrictions on the use of this code other
|
16
|
+
* than to include my name in any derived work. There are
|
17
|
+
* no warranty for this obviously, but you are welcomed
|
18
|
+
* to modify, correct, or adapt the code to your needs. The
|
19
|
+
* author appreciates if you are willing to submit corrections
|
20
|
+
* or suggestions for improvement.
|
21
|
+
*
|
22
|
+
*
|
23
|
+
* http://www.lazyhacker.com
|
24
|
+
*/
|
25
|
+
|
26
|
+
#include <iostream>
|
27
|
+
#include <cstdlib>
|
28
|
+
#include <stdio.h>
|
29
|
+
#include <string.h>
|
30
|
+
#include <sys/stat.h>
|
31
|
+
#include "gap_buffer.h"
|
32
|
+
|
33
|
+
using namespace std;
|
34
|
+
|
35
|
+
GapBuffer::GapBuffer(int gsize) : GAP_SIZE(gsize)
|
36
|
+
{
|
37
|
+
InitBuffer(GAP_SIZE);
|
38
|
+
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
GapBuffer::GapBuffer(FILE *file, int gsize) : GAP_SIZE(gsize)
|
43
|
+
{
|
44
|
+
|
45
|
+
// determine the size of the file then create
|
46
|
+
// a buffer of size + GAP_SIZE
|
47
|
+
struct stat buf;
|
48
|
+
|
49
|
+
fstat(fileno(file), &buf);
|
50
|
+
long file_size = buf.st_size;
|
51
|
+
InitBuffer(file_size + GAP_SIZE);
|
52
|
+
MoveGapToPoint();
|
53
|
+
ExpandGap( (int)file_size );
|
54
|
+
unsigned int amount = fread(gapstart, 1, file_size, file);
|
55
|
+
|
56
|
+
gapstart += amount;
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
}
|
61
|
+
|
62
|
+
/* Copy Constructor - Since we have pointers
|
63
|
+
* as member data, we need to provide our own
|
64
|
+
* copy constructor because the default will
|
65
|
+
* only cause copies to all point to the same
|
66
|
+
* location.
|
67
|
+
*/
|
68
|
+
GapBuffer::GapBuffer(const GapBuffer& tb)
|
69
|
+
{
|
70
|
+
GAP_SIZE = tb.GAP_SIZE;
|
71
|
+
|
72
|
+
buffer = (char *) malloc(tb.bufend - tb.buffer);
|
73
|
+
|
74
|
+
strcpy(buffer, tb.buffer);
|
75
|
+
|
76
|
+
bufend = buffer + (tb.bufend - tb.buffer);
|
77
|
+
gapstart = buffer + (tb.gapstart - tb.buffer);
|
78
|
+
gapend = gapstart + (tb.gapend - tb.gapstart);
|
79
|
+
point = buffer + (tb.point - tb.buffer);
|
80
|
+
|
81
|
+
}
|
82
|
+
|
83
|
+
|
84
|
+
GapBuffer::~GapBuffer()
|
85
|
+
{
|
86
|
+
|
87
|
+
if (buffer) {
|
88
|
+
free(buffer);
|
89
|
+
}
|
90
|
+
|
91
|
+
}
|
92
|
+
|
93
|
+
/*
|
94
|
+
* Copy the characters from one location to another. We have
|
95
|
+
* to write our own instead of using memcopy because we are
|
96
|
+
* working within a single linear buffer and thus can have
|
97
|
+
* overlap between the source and destination.
|
98
|
+
*/
|
99
|
+
int GapBuffer::CopyBytes(char *destination, char *source, unsigned int length)
|
100
|
+
{
|
101
|
+
|
102
|
+
if ( (destination == source) || (length == 0) ) {
|
103
|
+
return 1;
|
104
|
+
}
|
105
|
+
|
106
|
+
// if we're moving the character toward the front of the buffer
|
107
|
+
if (source > destination) {
|
108
|
+
|
109
|
+
// check to make sure that we don't go beyond the buffer
|
110
|
+
if ( (source + length) >= bufend ) {
|
111
|
+
return 0;
|
112
|
+
}
|
113
|
+
|
114
|
+
for (; length > 0; length--) {
|
115
|
+
*(destination++) = *(source++);
|
116
|
+
}
|
117
|
+
|
118
|
+
} else {
|
119
|
+
|
120
|
+
// To prevent overwriting characters we still
|
121
|
+
// need to move, go to the back and copy forward.
|
122
|
+
source += length;
|
123
|
+
destination += length;
|
124
|
+
|
125
|
+
for (; length > 0; length--) {
|
126
|
+
// decrement first 'cause we start one byte beyond where we want
|
127
|
+
*(--destination) = *(--source);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
return 1;
|
132
|
+
|
133
|
+
}
|
134
|
+
|
135
|
+
/*
|
136
|
+
* Expand the buffer to new size + GAP_SIZE.
|
137
|
+
*
|
138
|
+
*/
|
139
|
+
void GapBuffer::ExpandBuffer(unsigned int size)
|
140
|
+
{
|
141
|
+
|
142
|
+
// Check to see that we actually need to increase the buffer
|
143
|
+
// since BufferSize doesn't include the gap.
|
144
|
+
if ( ( (bufend - buffer) + size) > BufferSize() ) {
|
145
|
+
|
146
|
+
char *origbuffer = buffer;
|
147
|
+
|
148
|
+
int NewBufferSize = (bufend - buffer) + size + GAP_SIZE;
|
149
|
+
|
150
|
+
buffer = (char *) realloc(buffer, NewBufferSize);
|
151
|
+
|
152
|
+
point += buffer - origbuffer;
|
153
|
+
bufend += buffer - origbuffer;
|
154
|
+
gapstart += buffer - origbuffer;
|
155
|
+
gapend += buffer - origbuffer;
|
156
|
+
}
|
157
|
+
|
158
|
+
}
|
159
|
+
|
160
|
+
/*
|
161
|
+
* Move the gap to the current position of the point.
|
162
|
+
* The point should end in same location as gapstart.
|
163
|
+
*/
|
164
|
+
void GapBuffer::MoveGapToPoint()
|
165
|
+
{
|
166
|
+
|
167
|
+
|
168
|
+
if (point == gapstart) {
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
|
172
|
+
if (point == gapend) {
|
173
|
+
point = gapstart;
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
|
177
|
+
// Move gap towards the left
|
178
|
+
if (point < gapstart) {
|
179
|
+
// Move the point over by gapsize.
|
180
|
+
CopyBytes(point + (gapend-gapstart), point, gapstart - point);
|
181
|
+
gapend -= (gapstart - point);
|
182
|
+
gapstart = point;
|
183
|
+
} else {
|
184
|
+
// Since point is after the gap, find distance
|
185
|
+
// between gapend and point and that's how
|
186
|
+
// much we move from gapend to gapstart.
|
187
|
+
CopyBytes(gapstart, gapend, point - gapend);
|
188
|
+
gapstart += point - gapend;
|
189
|
+
gapend = point;
|
190
|
+
point = gapstart;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
/*
|
195
|
+
* Expand the size of the gap. If the required
|
196
|
+
* size is less then the current gap size, do
|
197
|
+
* nothing. If the size is greater than the
|
198
|
+
* current size, increase the gap to the default
|
199
|
+
* gap size + size.
|
200
|
+
*/
|
201
|
+
void GapBuffer::ExpandGap(unsigned int size)
|
202
|
+
{
|
203
|
+
|
204
|
+
|
205
|
+
if (size > SizeOfGap()) {
|
206
|
+
size += GAP_SIZE;
|
207
|
+
ExpandBuffer(size);
|
208
|
+
CopyBytes(gapend+size, gapend, bufend - gapend);
|
209
|
+
|
210
|
+
gapend += size;
|
211
|
+
bufend += size;
|
212
|
+
}
|
213
|
+
|
214
|
+
}
|
215
|
+
|
216
|
+
/*
|
217
|
+
* Set point to offset from start of buffer.
|
218
|
+
*/
|
219
|
+
void GapBuffer::SetPoint(unsigned int offset)
|
220
|
+
{
|
221
|
+
|
222
|
+
point = buffer + offset;
|
223
|
+
|
224
|
+
if (point > gapstart) {
|
225
|
+
point += gapend - gapstart;
|
226
|
+
}
|
227
|
+
|
228
|
+
}
|
229
|
+
|
230
|
+
/*
|
231
|
+
* Returns the current size of the gap.
|
232
|
+
*/
|
233
|
+
int GapBuffer::SizeOfGap()
|
234
|
+
{
|
235
|
+
return gapend - gapstart;
|
236
|
+
|
237
|
+
}
|
238
|
+
|
239
|
+
/*
|
240
|
+
* Returns offset from point to start of buffer.
|
241
|
+
*/
|
242
|
+
unsigned int GapBuffer::PointOffset()
|
243
|
+
{
|
244
|
+
|
245
|
+
if (point > gapend) {
|
246
|
+
return ((point - buffer) - (gapend - gapstart));
|
247
|
+
} else {
|
248
|
+
return (point - buffer);
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
/*
|
253
|
+
* Return character that point is pointing to.
|
254
|
+
* If point is inside the gap, then return the
|
255
|
+
* the first character outside the gap.
|
256
|
+
*/
|
257
|
+
char GapBuffer::GetChar()
|
258
|
+
{
|
259
|
+
|
260
|
+
// If the point is anywhere in the gap, then
|
261
|
+
// it should always be at the start of the gap.
|
262
|
+
if (point == gapstart) {
|
263
|
+
point = gapend;
|
264
|
+
}
|
265
|
+
|
266
|
+
return *point;
|
267
|
+
}
|
268
|
+
|
269
|
+
/*
|
270
|
+
* Return the previous character and
|
271
|
+
* move point back one position.
|
272
|
+
*/
|
273
|
+
char GapBuffer::PreviousChar()
|
274
|
+
{
|
275
|
+
|
276
|
+
if (point == gapend) {
|
277
|
+
point = gapstart;
|
278
|
+
}
|
279
|
+
|
280
|
+
return *(--point);
|
281
|
+
}
|
282
|
+
|
283
|
+
/*
|
284
|
+
* Replace the character of point.
|
285
|
+
*/
|
286
|
+
void GapBuffer::ReplaceChar(char ch)
|
287
|
+
{
|
288
|
+
|
289
|
+
// Since we're just replacing the current character,
|
290
|
+
// we don't need to move or modify the gap.
|
291
|
+
if (point == gapstart) {
|
292
|
+
point = gapend;
|
293
|
+
}
|
294
|
+
|
295
|
+
if (point == bufend) {
|
296
|
+
ExpandBuffer(1);
|
297
|
+
bufend++;
|
298
|
+
}
|
299
|
+
|
300
|
+
*point = ch;
|
301
|
+
}
|
302
|
+
|
303
|
+
/*
|
304
|
+
* Increment pointer. Returns next character.
|
305
|
+
*/
|
306
|
+
char GapBuffer::NextChar()
|
307
|
+
{
|
308
|
+
// point should not be in the gap.
|
309
|
+
if (point == gapstart) {
|
310
|
+
point = gapend;
|
311
|
+
return *point;
|
312
|
+
}
|
313
|
+
|
314
|
+
return *(++point);
|
315
|
+
|
316
|
+
}
|
317
|
+
|
318
|
+
void GapBuffer::PutChar(char ch)
|
319
|
+
{
|
320
|
+
InsertChar(ch);
|
321
|
+
*point++;
|
322
|
+
|
323
|
+
}
|
324
|
+
|
325
|
+
/*
|
326
|
+
* Insert character at point position. Note
|
327
|
+
* that repeatedly calling this function will
|
328
|
+
* keep calling MoveGapToPoint since this function
|
329
|
+
* doesn't advance the point. The result is the
|
330
|
+
* text appears reverse of key strokes.
|
331
|
+
*/
|
332
|
+
void GapBuffer::InsertChar(char ch)
|
333
|
+
{
|
334
|
+
// Here we do need to move the gap if the point
|
335
|
+
// is not already at the start of the gap.
|
336
|
+
|
337
|
+
if (point != gapstart) {
|
338
|
+
MoveGapToPoint();
|
339
|
+
}
|
340
|
+
|
341
|
+
// check to make sure that the gap has room
|
342
|
+
if (gapstart == gapend) {
|
343
|
+
ExpandGap(1);
|
344
|
+
}
|
345
|
+
|
346
|
+
*(gapstart++) = ch;
|
347
|
+
|
348
|
+
}
|
349
|
+
|
350
|
+
/*
|
351
|
+
* Delete "size" number of characters.
|
352
|
+
*/
|
353
|
+
void GapBuffer::DeleteChars(unsigned int size)
|
354
|
+
{
|
355
|
+
|
356
|
+
if (point != gapstart) {
|
357
|
+
MoveGapToPoint();
|
358
|
+
}
|
359
|
+
|
360
|
+
// We shifted the gap so that gapend points to the location
|
361
|
+
// where we want to start deleting so extend it
|
362
|
+
// to cover all the characters.
|
363
|
+
gapend += size;
|
364
|
+
}
|
365
|
+
|
366
|
+
void GapBuffer::InsertString(char *string, unsigned int length)
|
367
|
+
{
|
368
|
+
|
369
|
+
MoveGapToPoint();
|
370
|
+
|
371
|
+
if (length > SizeOfGap()) {
|
372
|
+
ExpandGap(length);
|
373
|
+
}
|
374
|
+
|
375
|
+
do {
|
376
|
+
PutChar(*(string++));
|
377
|
+
} while ( length-- );
|
378
|
+
}
|
379
|
+
|
380
|
+
/*
|
381
|
+
* Here we initilize the buffer and set
|
382
|
+
* the pointers to the correct position.
|
383
|
+
*/
|
384
|
+
int GapBuffer::InitBuffer(unsigned int size)
|
385
|
+
{
|
386
|
+
|
387
|
+
/*
|
388
|
+
if (buffer) {
|
389
|
+
free(buffer);
|
390
|
+
}
|
391
|
+
*/
|
392
|
+
buffer = NULL;
|
393
|
+
buffer = (char *) malloc(size);
|
394
|
+
|
395
|
+
if (!buffer) {
|
396
|
+
return 0;
|
397
|
+
}
|
398
|
+
|
399
|
+
point = buffer;
|
400
|
+
gapstart = buffer;
|
401
|
+
|
402
|
+
// initially gapend is outside of buffer
|
403
|
+
gapend = buffer + size;
|
404
|
+
bufend = gapend;
|
405
|
+
|
406
|
+
return 1;
|
407
|
+
|
408
|
+
}
|
409
|
+
|
410
|
+
int GapBuffer::BufferSize()
|
411
|
+
{
|
412
|
+
return (bufend - buffer) - (gapend - gapstart);
|
413
|
+
}
|
414
|
+
|
415
|
+
char* GapBuffer::GetBuffer()
|
416
|
+
{
|
417
|
+
return buffer;
|
418
|
+
}
|
419
|
+
|
420
|
+
void GapBuffer::PrintBuffer()
|
421
|
+
{
|
422
|
+
/*
|
423
|
+
char ch;
|
424
|
+
|
425
|
+
cout << "Printing the buffer: " << endl;
|
426
|
+
SetPoint(0);
|
427
|
+
while (point < bufend) {
|
428
|
+
cout << GetCharMovePoint();
|
429
|
+
}
|
430
|
+
|
431
|
+
cout << "Printing the buffer in reverse: " << endl;
|
432
|
+
|
433
|
+
while (point >= buffer) {
|
434
|
+
cout << GetPrevCharMovePoint();
|
435
|
+
}
|
436
|
+
*/
|
437
|
+
|
438
|
+
char *temp = buffer;
|
439
|
+
|
440
|
+
|
441
|
+
while (temp < bufend) {
|
442
|
+
|
443
|
+
if ( (temp >= gapstart) && (temp < gapend) ) {
|
444
|
+
cout << "_";
|
445
|
+
temp++;
|
446
|
+
} else {
|
447
|
+
cout << *(temp++);
|
448
|
+
}
|
449
|
+
|
450
|
+
}
|
451
|
+
cout << endl;
|
452
|
+
}
|
453
|
+
|
454
|
+
int GapBuffer::SaveBufferToFile(FILE *file, unsigned int bytes)
|
455
|
+
{
|
456
|
+
|
457
|
+
if (!bytes) {
|
458
|
+
return 1;
|
459
|
+
}
|
460
|
+
|
461
|
+
if (point == gapstart) {
|
462
|
+
point = gapend;
|
463
|
+
}
|
464
|
+
|
465
|
+
if ( (gapstart > point) && (gapstart < (point + bytes)) && (gapstart != gapend) ) {
|
466
|
+
if ( gapstart - point != fwrite(point, 1, gapstart-point, file) ) {
|
467
|
+
return 0;
|
468
|
+
}
|
469
|
+
|
470
|
+
if ( (bytes - (gapstart - point)) != fwrite(gapend, 1, bytes-(gapstart - point), file) ) {
|
471
|
+
return 1;
|
472
|
+
}
|
473
|
+
|
474
|
+
return 1;
|
475
|
+
} else {
|
476
|
+
return bytes == fwrite(point, 1, bytes, file);
|
477
|
+
}
|
478
|
+
|
479
|
+
|
480
|
+
}
|
@@ -0,0 +1,159 @@
|
|
1
|
+
/*
|
2
|
+
* gap_buffer.h
|
3
|
+
*
|
4
|
+
* Author: Hsin Tsao (stsao@lazyhacker.com)
|
5
|
+
* Version: 1.0 (June 12, 2003)
|
6
|
+
*
|
7
|
+
* A text buffer class using the buffer-gap technique for managing
|
8
|
+
* the text stored in the buffer.
|
9
|
+
*
|
10
|
+
* Portions of this work derived from Joseph Allen's usenet
|
11
|
+
* postings on comp.editor that was released to the public
|
12
|
+
* domain.
|
13
|
+
*
|
14
|
+
*
|
15
|
+
* There are no restrictions on the use of this code other
|
16
|
+
* than to include my name in any derived work. There are
|
17
|
+
* no warranty for this obviously, but you are welcomed
|
18
|
+
* to modify, correct, or adapt the code to your needs. The
|
19
|
+
* author appreciates if you are willing to submit corrections
|
20
|
+
* or suggestions for improvement.
|
21
|
+
*
|
22
|
+
*
|
23
|
+
* http://www.lazyhacker.com
|
24
|
+
*/
|
25
|
+
|
26
|
+
#include <stdio.h>
|
27
|
+
|
28
|
+
class GapBuffer {
|
29
|
+
|
30
|
+
char *point; // location pointer into buffer
|
31
|
+
char *buffer; // start of text buffer
|
32
|
+
char *bufend; // first location outside buffer
|
33
|
+
char *gapstart; // start of gap
|
34
|
+
char *gapend; // first location after end of gap
|
35
|
+
|
36
|
+
unsigned int GAP_SIZE; // expand GAP by this value
|
37
|
+
|
38
|
+
int InitBuffer(unsigned int size);
|
39
|
+
|
40
|
+
/*
|
41
|
+
* Copy the characters from one location to another. We have
|
42
|
+
* to write our own instead of using memcopy because we are
|
43
|
+
* working within a single linear buffer and thus can have
|
44
|
+
* overlap between the source and destination.
|
45
|
+
*/
|
46
|
+
int CopyBytes(char *destination, char *source, unsigned int length);
|
47
|
+
|
48
|
+
/*
|
49
|
+
* Expand the size of the buffer.
|
50
|
+
*/
|
51
|
+
void ExpandBuffer(unsigned int size);
|
52
|
+
|
53
|
+
/*
|
54
|
+
* Expand the size of the gap.
|
55
|
+
*/
|
56
|
+
void ExpandGap(unsigned int size);
|
57
|
+
|
58
|
+
public:
|
59
|
+
|
60
|
+
static const int DEFAULT_GAP_SIZE=20;
|
61
|
+
|
62
|
+
/* Constructor with default gap size. */
|
63
|
+
GapBuffer(int gsize=DEFAULT_GAP_SIZE);
|
64
|
+
|
65
|
+
/* Constructor with instantiating with an existing file. */
|
66
|
+
GapBuffer(FILE *file, int gsize=DEFAULT_GAP_SIZE);
|
67
|
+
|
68
|
+
/* Copy constructor to deal with our pointer members. */
|
69
|
+
GapBuffer(const GapBuffer& tb);
|
70
|
+
|
71
|
+
~GapBuffer();
|
72
|
+
|
73
|
+
/*
|
74
|
+
* Returns the size of the buffer minus the gap.
|
75
|
+
*/
|
76
|
+
int BufferSize();
|
77
|
+
|
78
|
+
/*
|
79
|
+
* Move the gap to the current position of the point.
|
80
|
+
*/
|
81
|
+
void MoveGapToPoint();
|
82
|
+
|
83
|
+
/*
|
84
|
+
* Set point to offset from start of buffer.
|
85
|
+
*/
|
86
|
+
void SetPoint(unsigned int offset);
|
87
|
+
|
88
|
+
/*
|
89
|
+
* Returns the current size of the gap.
|
90
|
+
*/
|
91
|
+
int SizeOfGap();
|
92
|
+
|
93
|
+
/*
|
94
|
+
* Returns offset from point to start of buffer.
|
95
|
+
*/
|
96
|
+
unsigned int PointOffset();
|
97
|
+
|
98
|
+
/*
|
99
|
+
* Return character that point is pointing to.
|
100
|
+
* If point is inside the gap, then return the
|
101
|
+
* the first character outside the gap.
|
102
|
+
*/
|
103
|
+
char GetChar();
|
104
|
+
|
105
|
+
/*
|
106
|
+
* Return the previous character and
|
107
|
+
* move point back one position.
|
108
|
+
*/
|
109
|
+
char PreviousChar();
|
110
|
+
|
111
|
+
/*
|
112
|
+
* Replace the character of point. Does
|
113
|
+
* not move the gap.
|
114
|
+
*/
|
115
|
+
void ReplaceChar(char ch);
|
116
|
+
|
117
|
+
/*
|
118
|
+
* Get the next character and increment point.
|
119
|
+
*/
|
120
|
+
char NextChar();
|
121
|
+
|
122
|
+
/*
|
123
|
+
* Inserts a character at point location
|
124
|
+
* and advance the point.
|
125
|
+
*/
|
126
|
+
void PutChar(char ch);
|
127
|
+
|
128
|
+
/*
|
129
|
+
* Insert character at point position, but
|
130
|
+
* does NOT advance the point.
|
131
|
+
*/
|
132
|
+
void InsertChar(char ch);
|
133
|
+
|
134
|
+
/*
|
135
|
+
* Delete "size" number of characters.
|
136
|
+
*/
|
137
|
+
void DeleteChars(unsigned int size);
|
138
|
+
|
139
|
+
/*
|
140
|
+
* Inserts a length size string
|
141
|
+
* at point.
|
142
|
+
*/
|
143
|
+
void InsertString(char *string, unsigned int length);
|
144
|
+
|
145
|
+
/*
|
146
|
+
* Prints out the current buffer from start
|
147
|
+
* to end.
|
148
|
+
*/
|
149
|
+
void PrintBuffer();
|
150
|
+
|
151
|
+
/*
|
152
|
+
* Saves to file the number of bytes starting from
|
153
|
+
* the point.
|
154
|
+
*/
|
155
|
+
int SaveBufferToFile(FILE *file, unsigned int bytes);
|
156
|
+
|
157
|
+
char *GetBuffer();
|
158
|
+
|
159
|
+
};
|
@@ -0,0 +1,191 @@
|
|
1
|
+
#include "gap_buffer.h"
|
2
|
+
#include <ruby.h>
|
3
|
+
|
4
|
+
#ifndef RSTRING_PTR
|
5
|
+
#define RSTRING_PTR(obj) RSTRING(obj)->ptr
|
6
|
+
#endif
|
7
|
+
|
8
|
+
#ifndef RSTRING_LEN
|
9
|
+
#define RSTRING_LEN(obj) RSTRING(obj)->len
|
10
|
+
#endif
|
11
|
+
|
12
|
+
#define GetGapBuffer(obj) \
|
13
|
+
GapBuffer *gb = NULL; \
|
14
|
+
Data_Get_Struct((obj), GapBuffer, gb); \
|
15
|
+
if (!gb) \
|
16
|
+
rb_raise(rb_eException, "No gap buffer!");
|
17
|
+
|
18
|
+
#define GapBufferChar(chr) \
|
19
|
+
char c = (chr); \
|
20
|
+
if (c == 0) return rb_str_tmp_new(0); \
|
21
|
+
return rb_str_new(&c, 1);
|
22
|
+
|
23
|
+
VALUE rb_cGapBuffer;
|
24
|
+
|
25
|
+
static void rb_gap_buffer_free(void *gb)
|
26
|
+
{
|
27
|
+
if(gb) delete((GapBuffer*) gb);
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE rb_gap_buffer_new(int argc, VALUE *argv, VALUE obj)
|
31
|
+
{
|
32
|
+
VALUE size;
|
33
|
+
rb_scan_args(argc, argv, "01", &size);
|
34
|
+
if (NIL_P(size)) size = rb_const_get(rb_cGapBuffer, rb_intern("GAP_SIZE"));
|
35
|
+
GapBuffer *gb = NULL;
|
36
|
+
gb = new GapBuffer(NUM2INT(size));
|
37
|
+
if (!gb)
|
38
|
+
rb_raise(rb_eException, "No gap buffer!");
|
39
|
+
return Data_Wrap_Struct(rb_cGapBuffer, 0, rb_gap_buffer_free, (void*)gb);
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE rb_gap_buffer_size(VALUE obj)
|
43
|
+
{
|
44
|
+
GetGapBuffer(obj);
|
45
|
+
return INT2FIX(gb->BufferSize());
|
46
|
+
}
|
47
|
+
|
48
|
+
static VALUE rb_gap_buffer_gap_size(VALUE obj)
|
49
|
+
{
|
50
|
+
GetGapBuffer(obj);
|
51
|
+
return INT2FIX(gb->SizeOfGap());
|
52
|
+
}
|
53
|
+
|
54
|
+
static VALUE rb_gap_buffer_print(VALUE obj)
|
55
|
+
{
|
56
|
+
GetGapBuffer(obj);
|
57
|
+
gb->PrintBuffer();
|
58
|
+
return Qnil;
|
59
|
+
}
|
60
|
+
|
61
|
+
static VALUE rb_gap_buffer_append(VALUE obj, VALUE str)
|
62
|
+
{
|
63
|
+
GetGapBuffer(obj);
|
64
|
+
Check_Type(str, T_STRING);
|
65
|
+
gb->InsertString(RSTRING_PTR(str), (unsigned int)RSTRING_LEN(str));
|
66
|
+
return obj;
|
67
|
+
}
|
68
|
+
|
69
|
+
static VALUE rb_gap_buffer_offset(VALUE obj)
|
70
|
+
{
|
71
|
+
GetGapBuffer(obj);
|
72
|
+
return INT2FIX(gb->PointOffset());
|
73
|
+
}
|
74
|
+
|
75
|
+
static VALUE rb_gap_buffer_offset_equals(VALUE obj, VALUE offset)
|
76
|
+
{
|
77
|
+
GetGapBuffer(obj);
|
78
|
+
gb->SetPoint(FIX2INT(offset));
|
79
|
+
return offset;
|
80
|
+
}
|
81
|
+
|
82
|
+
static VALUE rb_gap_buffer_move(VALUE obj)
|
83
|
+
{
|
84
|
+
GetGapBuffer(obj);
|
85
|
+
gb->MoveGapToPoint();
|
86
|
+
return rb_gap_buffer_offset(obj);
|
87
|
+
}
|
88
|
+
|
89
|
+
static VALUE rb_gap_buffer_get(VALUE obj)
|
90
|
+
{
|
91
|
+
GetGapBuffer(obj);
|
92
|
+
GapBufferChar(gb->GetChar());
|
93
|
+
}
|
94
|
+
|
95
|
+
static VALUE rb_gap_buffer_previous(VALUE obj)
|
96
|
+
{
|
97
|
+
GetGapBuffer(obj);
|
98
|
+
GapBufferChar(gb->PreviousChar());
|
99
|
+
}
|
100
|
+
|
101
|
+
static VALUE rb_gap_buffer_next(VALUE obj)
|
102
|
+
{
|
103
|
+
GetGapBuffer(obj);
|
104
|
+
GapBufferChar(gb->NextChar());
|
105
|
+
}
|
106
|
+
|
107
|
+
static VALUE rb_gap_buffer_replace(VALUE obj, VALUE str)
|
108
|
+
{
|
109
|
+
GetGapBuffer(obj);
|
110
|
+
Check_Type(str, T_STRING);
|
111
|
+
char c = *RSTRING_PTR(str);
|
112
|
+
gb->ReplaceChar(c);
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
|
116
|
+
static VALUE rb_gap_buffer_put(VALUE obj, VALUE str)
|
117
|
+
{
|
118
|
+
GetGapBuffer(obj);
|
119
|
+
Check_Type(str, T_STRING);
|
120
|
+
char c = *RSTRING_PTR(str);
|
121
|
+
gb->PutChar(c);
|
122
|
+
return Qnil;
|
123
|
+
}
|
124
|
+
|
125
|
+
static VALUE rb_gap_buffer_insert(VALUE obj, VALUE str)
|
126
|
+
{
|
127
|
+
GetGapBuffer(obj);
|
128
|
+
Check_Type(str, T_STRING);
|
129
|
+
char c = *RSTRING_PTR(str);
|
130
|
+
gb->InsertChar(c);
|
131
|
+
return Qnil;
|
132
|
+
}
|
133
|
+
|
134
|
+
static VALUE rb_gap_buffer_delete(VALUE obj, VALUE size)
|
135
|
+
{
|
136
|
+
GetGapBuffer(obj);
|
137
|
+
gb->DeleteChars(FIX2INT(size));
|
138
|
+
return Qnil;
|
139
|
+
}
|
140
|
+
|
141
|
+
static VALUE rb_gap_buffer_to_s(VALUE obj, VALUE size)
|
142
|
+
{
|
143
|
+
GetGapBuffer(obj);
|
144
|
+
return(rb_str_new2(gb->GetBuffer()));
|
145
|
+
}
|
146
|
+
|
147
|
+
static VALUE rb_gap_buffer_insert_at(VALUE obj, VALUE offset, VALUE str)
|
148
|
+
{
|
149
|
+
GetGapBuffer(obj);
|
150
|
+
Check_Type(str, T_STRING);
|
151
|
+
char c = *RSTRING_PTR(str);
|
152
|
+
gb->SetPoint(FIX2INT(offset));
|
153
|
+
gb->InsertChar(c);
|
154
|
+
return Qnil;
|
155
|
+
}
|
156
|
+
|
157
|
+
static VALUE rb_gap_buffer_delete_at(VALUE obj, VALUE offset)
|
158
|
+
{
|
159
|
+
GetGapBuffer(obj);
|
160
|
+
gb->SetPoint(FIX2INT(offset));
|
161
|
+
gb->DeleteChars(1);
|
162
|
+
return Qnil;
|
163
|
+
}
|
164
|
+
|
165
|
+
extern "C" void Init_gap_buffer()
|
166
|
+
{
|
167
|
+
rb_cGapBuffer = rb_define_class("GapBuffer", rb_cObject);
|
168
|
+
rb_define_const(rb_cGapBuffer, "GAP_SIZE", INT2NUM(20));
|
169
|
+
|
170
|
+
rb_define_module_function(rb_cGapBuffer, "new", (VALUE(*)(...))rb_gap_buffer_new, -1);
|
171
|
+
rb_define_method(rb_cGapBuffer, "size", (VALUE(*)(...))rb_gap_buffer_size, 0);
|
172
|
+
rb_define_method(rb_cGapBuffer, "gap_size", (VALUE(*)(...))rb_gap_buffer_gap_size, 0);
|
173
|
+
rb_define_method(rb_cGapBuffer, "print", (VALUE(*)(...))rb_gap_buffer_print, 0);
|
174
|
+
rb_define_method(rb_cGapBuffer, "<<", (VALUE(*)(...))rb_gap_buffer_append, 1);
|
175
|
+
rb_define_method(rb_cGapBuffer, "offset", (VALUE(*)(...))rb_gap_buffer_offset, 0);
|
176
|
+
rb_define_method(rb_cGapBuffer, "position", (VALUE(*)(...))rb_gap_buffer_offset, 0);
|
177
|
+
rb_define_method(rb_cGapBuffer, "offset=", (VALUE(*)(...))rb_gap_buffer_offset_equals, 1);
|
178
|
+
rb_define_method(rb_cGapBuffer, "position=", (VALUE(*)(...))rb_gap_buffer_offset_equals, 1);
|
179
|
+
rb_define_method(rb_cGapBuffer, "move", (VALUE(*)(...))rb_gap_buffer_move, 0);
|
180
|
+
rb_define_method(rb_cGapBuffer, "get", (VALUE(*)(...))rb_gap_buffer_get, 0);
|
181
|
+
rb_define_method(rb_cGapBuffer, "previous", (VALUE(*)(...))rb_gap_buffer_previous, 0);
|
182
|
+
rb_define_method(rb_cGapBuffer, "next", (VALUE(*)(...))rb_gap_buffer_next, 0);
|
183
|
+
rb_define_method(rb_cGapBuffer, "replace", (VALUE(*)(...))rb_gap_buffer_replace, 1);
|
184
|
+
rb_define_method(rb_cGapBuffer, "put", (VALUE(*)(...))rb_gap_buffer_put, 1);
|
185
|
+
rb_define_method(rb_cGapBuffer, "insert", (VALUE(*)(...))rb_gap_buffer_insert, 1);
|
186
|
+
rb_define_method(rb_cGapBuffer, "delete", (VALUE(*)(...))rb_gap_buffer_delete, 1);
|
187
|
+
rb_define_method(rb_cGapBuffer, "to_s", (VALUE(*)(...))rb_gap_buffer_to_s, 0);
|
188
|
+
rb_define_method(rb_cGapBuffer, "to_str", (VALUE(*)(...))rb_gap_buffer_to_s, 0);
|
189
|
+
rb_define_method(rb_cGapBuffer, "insert_at", (VALUE(*)(...))rb_gap_buffer_insert_at, 2);
|
190
|
+
rb_define_method(rb_cGapBuffer, "delete_at", (VALUE(*)(...))rb_gap_buffer_delete_at, 1);
|
191
|
+
}
|
data/gap_buffer.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'gap_buffer'
|
3
|
+
s.version = '0.1'
|
4
|
+
s.date = '2010-10-25'
|
5
|
+
s.authors = ['Lourens Naudé', 'Hsin Tsao']
|
6
|
+
s.email = ['lourens@methodmissing.com', 'stsao@lazyhacker.com']
|
7
|
+
s.description = 'WIP implementation of a Gap Buffer for Ruby MRI.'
|
8
|
+
s.homepage = 'http://github.com/methodmissing/gap_buffer'
|
9
|
+
s.summary = 'WIP implementation of a Gap Buffer for Ruby MRI.'
|
10
|
+
s.extensions = 'ext/gap_buffer/extconf.rb'
|
11
|
+
s.files = Dir.glob("{ext,test,bench}/**/*") + %w[README Rakefile gap_buffer.gemspec]
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = Dir['ext/gap_buffer/*.c']
|
14
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
class TestGapBuffer < Test::Unit::TestCase
|
2
|
+
def test_initialize
|
3
|
+
assert_equal 20, GapBuffer::GAP_SIZE
|
4
|
+
assert_instance_of GapBuffer, GapBuffer.new(10)
|
5
|
+
assert_equal 10, GapBuffer.new(10).gap_size
|
6
|
+
assert_equal 20, GapBuffer.new.gap_size
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_size
|
10
|
+
gb = GapBuffer.new
|
11
|
+
assert_equal 0, gb.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_gap_size
|
15
|
+
gb = GapBuffer.new
|
16
|
+
assert_equal 20, gb.gap_size
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_print
|
20
|
+
gb = GapBuffer.new
|
21
|
+
gb.print
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_append
|
25
|
+
gb = GapBuffer.new
|
26
|
+
gb << 'test'
|
27
|
+
assert_equal 'test', gb.to_s
|
28
|
+
assert_equal 5, gb.offset
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_offset
|
32
|
+
gb = GapBuffer.new
|
33
|
+
assert_equal 0, gb.offset
|
34
|
+
gb << 'test'
|
35
|
+
assert_equal 5, gb.offset
|
36
|
+
gb << 'buffer'
|
37
|
+
assert_equal 12, gb.offset
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_offset_equals
|
41
|
+
gb = GapBuffer.new
|
42
|
+
assert_equal 0, gb.offset
|
43
|
+
gb.offset = 5
|
44
|
+
assert_equal 5, gb.offset
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_move
|
48
|
+
gb = GapBuffer.new
|
49
|
+
assert_equal 0, gb.offset
|
50
|
+
gb.offset = 5
|
51
|
+
gb.move
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_get
|
55
|
+
gb = GapBuffer.new
|
56
|
+
gb << 'test'
|
57
|
+
gb.offset = 2
|
58
|
+
assert_equal 's', gb.get
|
59
|
+
assert_equal 2, gb.offset
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_previous
|
63
|
+
gb = GapBuffer.new
|
64
|
+
gb << 'test'
|
65
|
+
assert_equal '', gb.previous
|
66
|
+
gb.offset = 2
|
67
|
+
assert_equal 'e', gb.previous
|
68
|
+
assert_equal 1, gb.offset
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_next
|
72
|
+
gb = GapBuffer.new
|
73
|
+
gb << 'test'
|
74
|
+
assert_equal '', gb.previous
|
75
|
+
gb.offset = 2
|
76
|
+
assert_equal 't', gb.next
|
77
|
+
assert_equal 3, gb.offset
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_delete
|
81
|
+
gb = GapBuffer.new
|
82
|
+
gb << 'test'
|
83
|
+
assert_equal '', gb.previous
|
84
|
+
gb.offset = 2
|
85
|
+
gb.delete(2)
|
86
|
+
assert_equal 'test', gb.to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_to_s
|
90
|
+
gb = GapBuffer.new
|
91
|
+
gb << 'test'
|
92
|
+
assert_equal 'test', gb.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_replace
|
96
|
+
gb = GapBuffer.new
|
97
|
+
gb << 'test'
|
98
|
+
gb.offset = 2
|
99
|
+
gb.replace('x')
|
100
|
+
assert_equal 'text', gb.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_insert
|
104
|
+
gb = GapBuffer.new
|
105
|
+
gb << 'test'
|
106
|
+
gb.offset = 2
|
107
|
+
gb.insert('x')
|
108
|
+
assert_equal 'text', gb.to_s
|
109
|
+
gb.insert('zz')
|
110
|
+
assert_equal 'tezt', gb.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_insert_at
|
114
|
+
gb = GapBuffer.new
|
115
|
+
gb << 'test'
|
116
|
+
gb.insert_at(2, 'x')
|
117
|
+
assert_equal 'text', gb.to_s
|
118
|
+
gb.insert_at(3, 'z')
|
119
|
+
assert_equal 'texz', gb.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_delete_at
|
123
|
+
gb = GapBuffer.new
|
124
|
+
gb << 'test'
|
125
|
+
gb.insert_at(2, 'x')
|
126
|
+
assert_equal 'text', gb.to_s
|
127
|
+
gb.delete_at(2)
|
128
|
+
assert_equal 'text', gb.to_s
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_put
|
132
|
+
gb = GapBuffer.new
|
133
|
+
gb << 'test'
|
134
|
+
gb.offset = 2
|
135
|
+
gb.put('x')
|
136
|
+
assert_equal 'text', gb.to_s
|
137
|
+
gb.put('zzzzz')
|
138
|
+
assert_equal 'texz', gb.to_s
|
139
|
+
end
|
140
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gap_buffer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
version: "0.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- "Lourens Naud\xC3\xA9"
|
12
|
+
- Hsin Tsao
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-25 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: WIP implementation of a Gap Buffer for Ruby MRI.
|
22
|
+
email:
|
23
|
+
- lourens@methodmissing.com
|
24
|
+
- stsao@lazyhacker.com
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions:
|
28
|
+
- ext/gap_buffer/extconf.rb
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- ext/gap_buffer/extconf.rb
|
33
|
+
- ext/gap_buffer/gap_buffer.cpp
|
34
|
+
- ext/gap_buffer/gap_buffer.h
|
35
|
+
- ext/gap_buffer/rubymain.cpp
|
36
|
+
- test/test.rb
|
37
|
+
- test/test_gap_buffer.rb
|
38
|
+
- bench/gap_buffer.rb
|
39
|
+
- README
|
40
|
+
- Rakefile
|
41
|
+
- gap_buffer.gemspec
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/methodmissing/gap_buffer
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.7
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: WIP implementation of a Gap Buffer for Ruby MRI.
|
74
|
+
test_files: []
|
75
|
+
|