cord 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +29 -0
- data/Rakefile +78 -0
- data/bench/cord.rb +25 -0
- data/bench/quiz.rb +52 -0
- data/cord.gemspec +14 -0
- data/ext/cord/cord.c +180 -0
- data/ext/cord/cord.h +35 -0
- data/ext/cord/extconf.rb +6 -0
- data/test/test.rb +3 -0
- data/test/test_cord.rb +71 -0
- metadata +76 -0
data/README
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Simple Concat Tree / Rope / Cord implementation for Ruby MRI
|
2
|
+
(c) 2010 Lourens Naudé (methodmissing)
|
3
|
+
|
4
|
+
http://github.com/methodmissing/cord
|
5
|
+
|
6
|
+
See http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf and http://en.wikipedia.org/wiki/Rope_(computer_science) for context.
|
7
|
+
|
8
|
+
This library works with Ruby 1.8 and 1.9 and exposes the following API :
|
9
|
+
|
10
|
+
c = Cord.new('test')
|
11
|
+
c.depth #=> 1
|
12
|
+
c << 'cord'
|
13
|
+
c.depth #=> 2
|
14
|
+
|
15
|
+
c = Cord.new('aaaa')
|
16
|
+
c << 'bbbb'
|
17
|
+
c.to_s #=> 'aaaabbbb'
|
18
|
+
|
19
|
+
To run the test suite:
|
20
|
+
|
21
|
+
rake
|
22
|
+
|
23
|
+
Todo:
|
24
|
+
|
25
|
+
Tree rebalance / normalization
|
26
|
+
Exploit shared string / COW semantics further
|
27
|
+
Cord#each
|
28
|
+
|
29
|
+
Work in progress, thanks for watching!
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/clean'
|
4
|
+
$:.unshift(File.expand_path('lib'))
|
5
|
+
CORD_ROOT = 'ext/cord'
|
6
|
+
|
7
|
+
desc 'Default: test'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run cord tests.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs = [CORD_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 "#{CORD_ROOT}/cord.c"
|
23
|
+
file "#{CORD_ROOT}/extconf.rb"
|
24
|
+
file "#{CORD_ROOT}/Makefile" => %W(#{CORD_ROOT}/cord.c #{CORD_ROOT}/extconf.rb) do
|
25
|
+
Dir.chdir(CORD_ROOT) do
|
26
|
+
ruby 'extconf.rb'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "generate makefile"
|
31
|
+
task :makefile => %W(#{CORD_ROOT}/Makefile #{CORD_ROOT}/cord.c)
|
32
|
+
|
33
|
+
dlext = Config::CONFIG['DLEXT']
|
34
|
+
file "#{CORD_ROOT}/cord.#{dlext}" => %W(#{CORD_ROOT}/Makefile #{CORD_ROOT}/cord.c) do
|
35
|
+
Dir.chdir(CORD_ROOT) do
|
36
|
+
sh 'make' # TODO - is there a config for which make somewhere?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "compile cord extension"
|
41
|
+
task :compile => "#{CORD_ROOT}/cord.#{dlext}"
|
42
|
+
|
43
|
+
task :clean do
|
44
|
+
Dir.chdir(CORD_ROOT) do
|
45
|
+
sh 'make clean'
|
46
|
+
end if File.exists?("#{CORD_ROOT}/Makefile")
|
47
|
+
end
|
48
|
+
|
49
|
+
CLEAN.include("#{CORD_ROOT}/Makefile")
|
50
|
+
CLEAN.include("#{CORD_ROOT}/cord.#{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(CORD_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/cord.rb"
|
70
|
+
end
|
71
|
+
task :bench => :build
|
72
|
+
|
73
|
+
desc 'Run qsort benchmarks from RubyQuiz'
|
74
|
+
task :bench_qsort do
|
75
|
+
ruby "bench/quiz.rb String"
|
76
|
+
ruby "bench/quiz.rb Cord"
|
77
|
+
end
|
78
|
+
task :bench_qsort => :build
|
data/bench/cord.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift "."
|
2
|
+
require 'benchmark'
|
3
|
+
require File.dirname(__FILE__) + '/../ext/cord/cord'
|
4
|
+
|
5
|
+
TESTS = 100_000
|
6
|
+
PAYLOADS = [
|
7
|
+
('a' * 5),
|
8
|
+
('b' * 10),
|
9
|
+
('c' * 20),
|
10
|
+
('d' * 40),
|
11
|
+
('e' * 80),
|
12
|
+
('f' * 120),
|
13
|
+
('g' * 200),
|
14
|
+
('h' * 300)
|
15
|
+
]
|
16
|
+
|
17
|
+
string = ''
|
18
|
+
cord = Cord.new
|
19
|
+
ary = []
|
20
|
+
|
21
|
+
Benchmark.bmbm do |results|
|
22
|
+
results.report("[cord] obj << a") { TESTS.times{ cord << PAYLOADS[rand(6)] } }
|
23
|
+
results.report("[str] obj << a") { TESTS.times{ string << PAYLOADS[rand(6)] } }
|
24
|
+
results.report("[ary] obj << a") { TESTS.times{ ary << PAYLOADS[rand(6)] } }
|
25
|
+
end
|
data/bench/quiz.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# from http://www.rubyquiz.com/quiz137.html
|
2
|
+
$:.unshift "."
|
3
|
+
require 'benchmark'
|
4
|
+
require File.dirname(__FILE__) + '/../ext/cord/cord'
|
5
|
+
|
6
|
+
#This code make a String/Rope of CHUNCKS chunks of text
|
7
|
+
#each chunck is SIZE bytes long. Each chunck starts with
|
8
|
+
#an 8 byte number. Initially the chuncks are shuffled the
|
9
|
+
#qsort method sorts them into ascending order.
|
10
|
+
#
|
11
|
+
#pass the name of the class to use as a parameter
|
12
|
+
#ruby -r rope.rb this_file Rope
|
13
|
+
|
14
|
+
puts 'preparing data...'
|
15
|
+
TextClass = Object.const_get(ARGV.shift || :String)
|
16
|
+
|
17
|
+
def qsort(text)
|
18
|
+
return TextClass.new if text.length == 0
|
19
|
+
pivot = text.slice(0,8).to_s.to_i
|
20
|
+
less = TextClass.new
|
21
|
+
more = TextClass.new
|
22
|
+
offset = 8+SIZE
|
23
|
+
while (offset < text.length)
|
24
|
+
i = text.slice(offset,8).to_s.to_i
|
25
|
+
(i < pivot ? less : more) << text.slice(offset,8+SIZE)
|
26
|
+
offset = offset + 8+SIZE
|
27
|
+
end
|
28
|
+
print "*"
|
29
|
+
return qsort(less) << text.slice(0,8+SIZE) << qsort(more)
|
30
|
+
end
|
31
|
+
|
32
|
+
SIZE = 512 * 1024
|
33
|
+
CHUNCKS = 128
|
34
|
+
CHARS = %w[R O P E]
|
35
|
+
data = TextClass.new
|
36
|
+
bulk_string =
|
37
|
+
TextClass.new(Array.new(SIZE) { CHARS[rand(4)] }.join)
|
38
|
+
puts 'Building Text...'
|
39
|
+
build = Benchmark.measure do
|
40
|
+
(0..CHUNCKS).sort_by { rand }.each do |n|
|
41
|
+
data<< sprintf("%08i",n) << bulk_string
|
42
|
+
end
|
43
|
+
data.normalize if data.respond_to? :normalize
|
44
|
+
end
|
45
|
+
GC.start
|
46
|
+
sort = Benchmark.measure do
|
47
|
+
puts "Sorting Text..."
|
48
|
+
qsort(data)
|
49
|
+
puts"\nEND"
|
50
|
+
end
|
51
|
+
|
52
|
+
puts "Build: #{build}Sort: #{sort}"
|
data/cord.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'cord'
|
3
|
+
s.version = '0.1'
|
4
|
+
s.date = '2010-10-23'
|
5
|
+
s.authors = ['Lourens Naudé']
|
6
|
+
s.email = ['lourens@methodmissing.com']
|
7
|
+
s.description = 'WIP implementation of a Concat Tree / Rope / Cord for Ruby MRI.'
|
8
|
+
s.homepage = 'http://github.com/methodmissing/rehash'
|
9
|
+
s.summary = 'WIP implementation of a Concat Tree / Rope / Cord for Ruby MRI.'
|
10
|
+
s.extensions = 'ext/cord/extconf.rb'
|
11
|
+
s.files = Dir.glob("{ext,test,bench}/**/*") + %w[README Rakefile cord.gemspec]
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = Dir['ext/cord/*.c']
|
14
|
+
end
|
data/ext/cord/cord.c
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include "cord.h"
|
3
|
+
|
4
|
+
static void rb_mark_cord_i(void *c)
|
5
|
+
{
|
6
|
+
CORD_WALK_GC(STRING_CORD_P(c), rb_mark_cord_i, rb_gc_mark_maybe, VALUE);
|
7
|
+
}
|
8
|
+
|
9
|
+
static void rb_mark_cord(RCord *c)
|
10
|
+
{
|
11
|
+
if (c) rb_mark_cord_i(c->cord);
|
12
|
+
}
|
13
|
+
|
14
|
+
static void rb_free_cord_i(void *c)
|
15
|
+
{
|
16
|
+
CORD_WALK_GC((!STRING_CORD_P(c)), rb_free_cord_i, xfree, void*);
|
17
|
+
}
|
18
|
+
|
19
|
+
static void rb_free_cord(RCord *c)
|
20
|
+
{
|
21
|
+
if (c){
|
22
|
+
rb_free_cord_i(c->cord);
|
23
|
+
xfree(c);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
static VALUE rb_cord_alloc(VALUE klass)
|
28
|
+
{
|
29
|
+
RCord *cs = NULL;
|
30
|
+
VALUE c;
|
31
|
+
c = Data_Make_Struct(klass, RCord, rb_mark_cord, rb_free_cord, cs);
|
32
|
+
cs->len = 0;
|
33
|
+
cs->depth = 0;
|
34
|
+
cs->cord = EMPTY_CORD;
|
35
|
+
return c;
|
36
|
+
}
|
37
|
+
|
38
|
+
static void *rb_cord_balance(void *cord)
|
39
|
+
{
|
40
|
+
return cord;
|
41
|
+
}
|
42
|
+
|
43
|
+
static char *rb_merge_cords(void *left, void *right)
|
44
|
+
{
|
45
|
+
char *cord = NULL;
|
46
|
+
long left_len, right_len, len;
|
47
|
+
left_len = CORD_LENGTH(left);
|
48
|
+
right_len = CORD_LENGTH(right);
|
49
|
+
len = left_len + right_len;
|
50
|
+
cord = ALLOC_N(char, len + 1);
|
51
|
+
memcpy(cord, RSTRING_PTR(left), left_len);
|
52
|
+
memcpy(cord + left_len, RSTRING_PTR(right), right_len);
|
53
|
+
cord[len] = '\0';
|
54
|
+
return cord;
|
55
|
+
}
|
56
|
+
|
57
|
+
static VALUE rb_new_flat_cord(void *left, void *right)
|
58
|
+
{
|
59
|
+
char *cord = NULL;
|
60
|
+
cord = rb_merge_cords(left, right);
|
61
|
+
return rb_str_new2(cord);
|
62
|
+
}
|
63
|
+
|
64
|
+
static void *rb_new_concat_cord(void * left, void * right){
|
65
|
+
RConcatCord *cord = NULL;
|
66
|
+
short int depth;
|
67
|
+
cord = ALLOC(RConcatCord);
|
68
|
+
cord->left = (void*)left;
|
69
|
+
cord->right = (void*)right;
|
70
|
+
cord->len = CORD_LENGTH(left) + RSTRING_LEN((VALUE)right);
|
71
|
+
depth = (CORD_DEPTH(right) > CORD_DEPTH(left)) ? CORD_DEPTH(right) : CORD_DEPTH(left);
|
72
|
+
cord->depth = depth + 1;
|
73
|
+
return cord;
|
74
|
+
}
|
75
|
+
|
76
|
+
static void *rb_append_cords(void * left, void *right)
|
77
|
+
{
|
78
|
+
RConcatCord *new_left = NULL;
|
79
|
+
RConcatCord *new_right = NULL;
|
80
|
+
if (CORD_LENGTH(left) + CORD_LENGTH(right) < STRING_CORD_THRESHOLD){
|
81
|
+
return (void *)rb_new_flat_cord(left, right);
|
82
|
+
}
|
83
|
+
if (!CONCAT_CORD_P(left) && CONCAT_CORD_P(right)){
|
84
|
+
new_right = CONCAT_CORD(right);
|
85
|
+
if (CORD_LENGTH(left) + CORD_LENGTH(new_right->left) < STRING_CORD_THRESHOLD){
|
86
|
+
return rb_new_concat_cord((void *)rb_new_flat_cord(left, new_right->left), new_right->right);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
if (!CONCAT_CORD_P(right) && CONCAT_CORD_P(left)){
|
90
|
+
new_left = CONCAT_CORD(left);
|
91
|
+
if (CORD_LENGTH(right) + CORD_LENGTH(new_left->right) < STRING_CORD_THRESHOLD){
|
92
|
+
return rb_new_concat_cord(new_left->left, (void *)rb_new_flat_cord(new_left->right, right));
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return rb_new_concat_cord(left, right);
|
96
|
+
}
|
97
|
+
|
98
|
+
static void concat_cord_to_s(VALUE buffer, void * cord)
|
99
|
+
{
|
100
|
+
if (EMPTY_CORD_P(cord) || STRING_CORD_P(cord)) return;
|
101
|
+
concat_cord_to_s(buffer, CONCAT_CORD(cord)->left);
|
102
|
+
concat_cord_to_s(buffer, CONCAT_CORD(cord)->right);
|
103
|
+
if (STRING_CORD_P(CONCAT_CORD(cord)->left)) rb_str_cat2(buffer, RSTRING_PTR(CONCAT_CORD(cord)->left));
|
104
|
+
rb_str_cat2(buffer, RSTRING_PTR(CONCAT_CORD(cord)->right));
|
105
|
+
}
|
106
|
+
|
107
|
+
static VALUE rb_cord_to_s(VALUE obj)
|
108
|
+
{
|
109
|
+
VALUE buffer;
|
110
|
+
RCord *c = GetCord(obj);
|
111
|
+
if (EMPTY_CORD_P(c->cord)) return rb_str_new2("");
|
112
|
+
if (STRING_CORD_P(c->cord)) return (VALUE)c->cord;
|
113
|
+
if (CONCAT_CORD_P(c->cord)){
|
114
|
+
buffer = rb_str_buf_new((long)CORD_LENGTH(c->cord));
|
115
|
+
concat_cord_to_s(buffer, c->cord);
|
116
|
+
return buffer;
|
117
|
+
}
|
118
|
+
return Qnil;
|
119
|
+
}
|
120
|
+
|
121
|
+
static VALUE rb_cord_append(VALUE obj, VALUE str)
|
122
|
+
{
|
123
|
+
RCord *c = GetCord(obj);
|
124
|
+
if (rb_obj_is_kind_of(str, rb_cCord)) str = rb_cord_to_s(str);
|
125
|
+
Check_Type(str, T_STRING);
|
126
|
+
str = rb_str_new4(str);
|
127
|
+
if (RSTRING_LEN(str) == 0) return Qnil;
|
128
|
+
if (EMPTY_CORD_P(c->cord)){
|
129
|
+
c->cord = (void *)str;
|
130
|
+
}else{
|
131
|
+
c->cord = rb_cord_balance(rb_append_cords((void *)c->cord, (void *)str));
|
132
|
+
}
|
133
|
+
return obj;
|
134
|
+
}
|
135
|
+
|
136
|
+
static VALUE rb_cord_length(VALUE obj)
|
137
|
+
{
|
138
|
+
RCord *c = GetCord(obj);
|
139
|
+
if (EMPTY_CORD_P(c->cord)) return INT2FIX(0);
|
140
|
+
return INT2FIX(CORD_LENGTH(c->cord));
|
141
|
+
}
|
142
|
+
|
143
|
+
static VALUE rb_cord_depth(VALUE obj)
|
144
|
+
{
|
145
|
+
RCord *c = GetCord(obj);
|
146
|
+
if (EMPTY_CORD_P(c->cord)) return INT2FIX(0);
|
147
|
+
return INT2FIX(CORD_DEPTH(c->cord));
|
148
|
+
}
|
149
|
+
|
150
|
+
static VALUE rb_cord_initialize(int argc, VALUE *argv, VALUE obj)
|
151
|
+
{
|
152
|
+
VALUE val;
|
153
|
+
RCord *c = GetCord(obj);
|
154
|
+
rb_scan_args(argc, argv, "01", &val);
|
155
|
+
if (!NIL_P(val)) rb_cord_append(obj, val);
|
156
|
+
return obj;
|
157
|
+
}
|
158
|
+
|
159
|
+
static VALUE
|
160
|
+
rb_cord_slice(int argc, VALUE *argv, VALUE obj)
|
161
|
+
{
|
162
|
+
VALUE str;
|
163
|
+
str = rb_cord_to_s(obj);
|
164
|
+
return rb_funcall2(str, rb_intern("slice"), argc, argv);
|
165
|
+
}
|
166
|
+
|
167
|
+
void
|
168
|
+
Init_cord()
|
169
|
+
{
|
170
|
+
rb_cCord = rb_define_class("Cord", rb_cObject);
|
171
|
+
rb_define_alloc_func(rb_cCord, rb_cord_alloc);
|
172
|
+
|
173
|
+
rb_define_method(rb_cCord, "initialize", rb_cord_initialize, -1);
|
174
|
+
rb_define_method(rb_cCord, "<<", rb_cord_append, 1);
|
175
|
+
rb_define_method(rb_cCord, "to_s", rb_cord_to_s, 0);
|
176
|
+
rb_define_method(rb_cCord, "to_str", rb_cord_to_s, 0);
|
177
|
+
rb_define_method(rb_cCord, "length", rb_cord_length, 0);
|
178
|
+
rb_define_method(rb_cCord, "depth", rb_cord_depth, 0);
|
179
|
+
rb_define_method(rb_cCord, "slice", rb_cord_slice, -1);
|
180
|
+
}
|
data/ext/cord/cord.h
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
typedef struct {
|
2
|
+
long len;
|
3
|
+
short int depth;
|
4
|
+
void *left, *right;
|
5
|
+
} RConcatCord;
|
6
|
+
|
7
|
+
typedef struct {
|
8
|
+
long len;
|
9
|
+
long depth;
|
10
|
+
void *cord;
|
11
|
+
} RCord;
|
12
|
+
|
13
|
+
VALUE rb_cCord;
|
14
|
+
|
15
|
+
#define STRING_CORD_THRESHOLD 17
|
16
|
+
#define MAX_CORD_DEPTH 96
|
17
|
+
|
18
|
+
#define GetCord(obj) (Check_Type(obj, T_DATA), (RCord*)DATA_PTR(obj))
|
19
|
+
|
20
|
+
#define EMPTY_CORD NULL
|
21
|
+
#define STRING_CORD(c) RSTRING(c)
|
22
|
+
#define CONCAT_CORD(c) ((RConcatCord *)(c))
|
23
|
+
#define EMPTY_CORD_P(c) (!(c))
|
24
|
+
#define STRING_CORD_P(c) (BUILTIN_TYPE(c) == T_STRING)
|
25
|
+
#define CONCAT_CORD_P(c) (!EMPTY_CORD_P(c) && !(STRING_CORD_P(c)))
|
26
|
+
#define CORD_LENGTH(c) (STRING_CORD_P(c) ? RSTRING_LEN(c) : CONCAT_CORD(c)->len)
|
27
|
+
#define CORD_DEPTH(c) (STRING_CORD_P(c) ? 1 : CONCAT_CORD(c)->depth)
|
28
|
+
#define CORD_WALK_GC(cond, iter, func, cast) \
|
29
|
+
if (c == NULL) return; \
|
30
|
+
if (CONCAT_CORD_P(c)){ \
|
31
|
+
(iter)(CONCAT_CORD(c)->left); \
|
32
|
+
(iter)(CONCAT_CORD(c)->right); \
|
33
|
+
}else{ \
|
34
|
+
if((cond)) (func)((cast)c); \
|
35
|
+
}
|
data/ext/cord/extconf.rb
ADDED
data/test/test.rb
ADDED
data/test/test_cord.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class TestCord < Test::Unit::TestCase
|
2
|
+
def test_append
|
3
|
+
c = Cord.new
|
4
|
+
assert_equal 'string', (c << 'string').to_s
|
5
|
+
oc = Cord.new('test')
|
6
|
+
assert_equal 'stringtest', (c << oc).to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_to_s_empty_cord
|
10
|
+
c = Cord.new
|
11
|
+
assert_equal '', c.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_append_non_string
|
15
|
+
c = Cord.new
|
16
|
+
assert_raises(TypeError){ c << :symbol }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_slice
|
20
|
+
c = Cord.new('test')
|
21
|
+
assert_equal 'te', c.slice(0..1)
|
22
|
+
assert_equal 'es', c.slice(1,2)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_length
|
26
|
+
c = Cord.new
|
27
|
+
assert_equal 0, c.length
|
28
|
+
c << 'cord'
|
29
|
+
assert_equal 4, c.length
|
30
|
+
c << 'test'
|
31
|
+
assert_equal 8, c.length
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_depth
|
35
|
+
c = Cord.new
|
36
|
+
assert_equal 0, c.depth
|
37
|
+
c << 'aaaa'
|
38
|
+
assert_equal 1, c.depth
|
39
|
+
c << 'bbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
40
|
+
assert_equal 2, c.depth
|
41
|
+
c << 'ccccccccccccccccccccccccccc'
|
42
|
+
assert_equal 3, c.depth
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_initialize
|
46
|
+
c = Cord.new
|
47
|
+
assert_instance_of Cord, c
|
48
|
+
assert_equal '', c.to_s
|
49
|
+
c = Cord.new('test')
|
50
|
+
assert_equal 'test', c.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_to_s
|
54
|
+
c = Cord.new
|
55
|
+
s = 'aaaa'
|
56
|
+
c << s
|
57
|
+
assert_equal 'aaaa', c.to_s
|
58
|
+
c << 'bbbb'
|
59
|
+
assert_equal 'aaaabbbb', c.to_s
|
60
|
+
c << ''
|
61
|
+
assert_equal 'aaaabbbb', c.to_s
|
62
|
+
c << 'cccccccccccccccccccccccccccccccccccc'
|
63
|
+
assert_equal "aaaabbbbcccccccccccccccccccccccccccccccccccc", c.to_s
|
64
|
+
c << 'ddddddddddddddddddddddddddddddddddddddd'
|
65
|
+
assert_equal 'aaaabbbbccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddd', c.to_s
|
66
|
+
c << 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
67
|
+
assert_equal 'aaaabbbbccccccccccccccccccccccccccccccccccccdddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', c.to_s
|
68
|
+
c << 'ffff'
|
69
|
+
assert_equal 'aaaabbbbccccccccccccccccccccccccccccccccccccdddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffff', c.to_s
|
70
|
+
end
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cord
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Lourens Naud\xC3\xA9"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-23 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: WIP implementation of a Concat Tree / Rope / Cord for Ruby MRI.
|
22
|
+
email:
|
23
|
+
- lourens@methodmissing.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions:
|
27
|
+
- ext/cord/extconf.rb
|
28
|
+
extra_rdoc_files:
|
29
|
+
- ext/cord/cord.c
|
30
|
+
files:
|
31
|
+
- ext/cord/cord.c
|
32
|
+
- ext/cord/cord.h
|
33
|
+
- ext/cord/extconf.rb
|
34
|
+
- test/test.rb
|
35
|
+
- test/test_cord.rb
|
36
|
+
- bench/cord.rb
|
37
|
+
- bench/quiz.rb
|
38
|
+
- README
|
39
|
+
- Rakefile
|
40
|
+
- cord.gemspec
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/methodmissing/rehash
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
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
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.3.7
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: WIP implementation of a Concat Tree / Rope / Cord for Ruby MRI.
|
75
|
+
test_files: []
|
76
|
+
|