gap_buffer 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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!
@@ -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
@@ -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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ $:.unshift "."
2
+ require 'ext/gap_buffer/gap_buffer'
3
+ require 'test/unit'
@@ -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
+