cord 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 +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
|
+
|