rod 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/README +144 -0
- data/Rakefile +58 -0
- data/changelog.txt +99 -0
- data/lib/rod.rb +21 -0
- data/lib/rod/abstract_database.rb +369 -0
- data/lib/rod/collection_proxy.rb +72 -0
- data/lib/rod/constants.rb +32 -0
- data/lib/rod/database.rb +598 -0
- data/lib/rod/exception.rb +56 -0
- data/lib/rod/join_element.rb +63 -0
- data/lib/rod/model.rb +926 -0
- data/lib/rod/segmented_index.rb +85 -0
- data/lib/rod/string_element.rb +37 -0
- data/lib/rod/string_ex.rb +14 -0
- data/rod.gemspec +28 -0
- metadata +167 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
module Rod
|
2
|
+
# This class allows for lazy fetching the objects from
|
3
|
+
# a collection of Rod objects. It holds only a Ruby proc, which
|
4
|
+
# called returns the object with given index.
|
5
|
+
class CollectionProxy
|
6
|
+
include Enumerable
|
7
|
+
attr_reader :size
|
8
|
+
alias count size
|
9
|
+
|
10
|
+
# Intializes the proxy with +size+ of the collection
|
11
|
+
# and +fetch+ block for retrieving the object from the database.
|
12
|
+
def initialize(size,&fetch)
|
13
|
+
@size = size
|
14
|
+
@original_size = size
|
15
|
+
@fetch = fetch
|
16
|
+
@appended = []
|
17
|
+
raise InvalidArgument.new("Cannot use proxy collection without a block!") unless block_given?
|
18
|
+
@proxy = SimpleWeakHash.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns an object with given +index+.
|
22
|
+
def [](index)
|
23
|
+
return nil if index >= @size
|
24
|
+
return @proxy[index] unless @proxy[index].nil?
|
25
|
+
rod_id, klass = id_and_class_for(index)
|
26
|
+
result = rod_id == 0 ? nil : klass.find_by_rod_id(rod_id)
|
27
|
+
@proxy[index] = result
|
28
|
+
end
|
29
|
+
|
30
|
+
# Appends element to the end of the collection.
|
31
|
+
def <<(rod_id_and_class)
|
32
|
+
@appended << rod_id_and_class
|
33
|
+
@size += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
# Simple each implementation.
|
37
|
+
def each
|
38
|
+
if block_given?
|
39
|
+
@size.times do |index|
|
40
|
+
yield self[index]
|
41
|
+
end
|
42
|
+
else
|
43
|
+
enum_for(:each)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Iterate over the rod_ids.
|
48
|
+
def each_id
|
49
|
+
if block_given?
|
50
|
+
@size.times do |index|
|
51
|
+
yield id_and_class_for(index)[0]
|
52
|
+
end
|
53
|
+
else
|
54
|
+
enum_for(:each_id)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# String representation.
|
59
|
+
def to_s
|
60
|
+
"Proxy:[#{@size}][#{@original_size}]"
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
def id_and_class_for(index)
|
65
|
+
if index >= @original_size && !@appended[index - @original_size].nil?
|
66
|
+
@appended[index - @original_size]
|
67
|
+
else
|
68
|
+
@fetch.call(index)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rod
|
2
|
+
VERSION = "0.6.0"
|
3
|
+
|
4
|
+
# The name of file containing the data base.
|
5
|
+
DATABASE_FILE = "database.yml"
|
6
|
+
|
7
|
+
# Invalid names of fields.
|
8
|
+
INVALID_NAMES = {"rod_id" => true}
|
9
|
+
|
10
|
+
TYPE_MAPPING = {
|
11
|
+
:string => 'char *',
|
12
|
+
:integer => 'long',
|
13
|
+
:float => 'double',
|
14
|
+
:ulong => 'unsigned long',
|
15
|
+
:object => 'char *'
|
16
|
+
}
|
17
|
+
|
18
|
+
RUBY_TO_C_MAPPING = {
|
19
|
+
:string => 'StringValuePtr',
|
20
|
+
:integer => 'NUM2LONG',
|
21
|
+
:float => 'NUM2DBL',
|
22
|
+
:ulong => 'NUM2ULONG'
|
23
|
+
}
|
24
|
+
|
25
|
+
C_TO_RUBY_MAPPING = {
|
26
|
+
:string => 'rb_str_new2',
|
27
|
+
:integer => 'INT2NUM',
|
28
|
+
:float => 'rb_float_new',
|
29
|
+
:ulong => 'ULONG2NUM'
|
30
|
+
}
|
31
|
+
|
32
|
+
end
|
data/lib/rod/database.rb
ADDED
@@ -0,0 +1,598 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'constants')
|
2
|
+
require 'rod/abstract_database'
|
3
|
+
|
4
|
+
module Rod
|
5
|
+
# This class implements (in C) the Database abstraction defined
|
6
|
+
# in Rod::Database.
|
7
|
+
#
|
8
|
+
# Instance of the class should not be used *as* the database (i.e.
|
9
|
+
# not in the macro-style function Rod::Model#database_class).
|
10
|
+
# A user should strongly consider subclassing it, since refraining
|
11
|
+
# from doing that, will not allow to use different databases for different
|
12
|
+
# models simultaneously. This is due to the way RubyInline creates and
|
13
|
+
# names (after the name of the class) the C code.
|
14
|
+
class Database < AbstractDatabase
|
15
|
+
# This flag indicates, if Database and Model works in development
|
16
|
+
# mode, i.e. the dynamically loaded library has a unique, different id each time
|
17
|
+
# the rod library is used.
|
18
|
+
@@rod_development_mode = false
|
19
|
+
|
20
|
+
# Writer of the +rod_development_mode+ flag.
|
21
|
+
def self.development_mode=(value)
|
22
|
+
@@rod_development_mode = value
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reader of the +rod_development_mode+ flag.
|
26
|
+
def self.development_mode
|
27
|
+
@@rod_development_mode
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
## Helper methods printing some generated code ##
|
33
|
+
|
34
|
+
def model_struct_name(path)
|
35
|
+
"model_" + path.gsub(/\W/,"_").squeeze("_")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Initializes the C structures, which are based on the classes.
|
39
|
+
def init_structs(classes)
|
40
|
+
classes.map do |klass|
|
41
|
+
<<-END
|
42
|
+
| // number of allocated pages
|
43
|
+
| model_p->#{klass.struct_name}_page_count = 0;
|
44
|
+
| // the number of allready stored structures
|
45
|
+
| model_p->#{klass.struct_name}_count = 0;
|
46
|
+
|
|
47
|
+
| // initialize the tables with NULL to forbid unmapping
|
48
|
+
| model_p->#{klass.struct_name}_table = NULL;
|
49
|
+
|
|
50
|
+
| // initialize the file descriptor to -1 to force its creation
|
51
|
+
| model_p->#{klass.struct_name}_lib_file = -1;
|
52
|
+
END
|
53
|
+
end.join("\n").margin
|
54
|
+
end
|
55
|
+
|
56
|
+
# Opens the file associated with the class. Creates it if it doesn't
|
57
|
+
# exist.
|
58
|
+
def open_class_file(klass)
|
59
|
+
str =<<-END
|
60
|
+
|// create the file unless it exists
|
61
|
+
|if(model_p->#{klass.struct_name}_lib_file == -1){
|
62
|
+
| char * path = malloc(sizeof(char) * (strlen(model_p->path) +
|
63
|
+
| #{klass.path_for_data("").size} + 1));
|
64
|
+
| strcpy(path,model_p->path);
|
65
|
+
| strcat(path,"#{klass.path_for_data("")}");
|
66
|
+
| model_p->#{klass.struct_name}_lib_file =
|
67
|
+
| open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
68
|
+
| if(model_p->#{klass.struct_name}_lib_file == -1) {
|
69
|
+
| rb_raise(rodException(),"Could not open file for class #{klass} on path %s writing.",path);
|
70
|
+
| }
|
71
|
+
| free(path);
|
72
|
+
|}
|
73
|
+
END
|
74
|
+
str.margin
|
75
|
+
end
|
76
|
+
|
77
|
+
# Updates the pointer to table of structs for a given +klass+ when
|
78
|
+
# the klass is (re)mmaped.
|
79
|
+
def update_pointer(klass)
|
80
|
+
str =<<-END
|
81
|
+
| {
|
82
|
+
| VALUE cClass = rb_cObject;
|
83
|
+
| #{klass.to_s.split("::").map do |name|
|
84
|
+
"cClass = rb_const_get(cClass, rb_intern(\"#{name}\"));"
|
85
|
+
end.join("\n| ")}
|
86
|
+
| rb_funcall(cClass, rb_intern("rod_pointer="),1,
|
87
|
+
| ULONG2NUM((unsigned long)model_p->#{klass.struct_name}_table));
|
88
|
+
| }
|
89
|
+
END
|
90
|
+
str.margin
|
91
|
+
end
|
92
|
+
|
93
|
+
# Mmaps the class to its page during database creation.
|
94
|
+
def mmap_class(klass)
|
95
|
+
str =<<-END
|
96
|
+
| //printf("mmaping #{klass}\\n");
|
97
|
+
| //unmap the segment(s) first
|
98
|
+
| if(model_p->#{klass.struct_name}_table != NULL){
|
99
|
+
| if(munmap(model_p->#{klass.struct_name}_table,
|
100
|
+
| page_size()*(model_p->#{klass.struct_name}_page_count)) == -1){
|
101
|
+
| perror(NULL);
|
102
|
+
| rb_raise(rodException(),"Could not unmap segment for #{klass.struct_name}.");
|
103
|
+
| }
|
104
|
+
| }
|
105
|
+
| \n#{open_class_file(klass)}
|
106
|
+
|
|
107
|
+
|
|
108
|
+
| // exted the file
|
109
|
+
|
|
110
|
+
| // increase the pages count by 1
|
111
|
+
| model_p->#{klass.struct_name}_page_count += ALLOCATED_PAGES;
|
112
|
+
|
|
113
|
+
| // open the file for writing
|
114
|
+
| FILE * #{klass.struct_name}_file =
|
115
|
+
| fdopen(model_p->#{klass.struct_name}_lib_file,"w+");
|
116
|
+
| if(#{klass.struct_name}_file == NULL){
|
117
|
+
| rb_raise(rodException(),"Could not open file for #{klass.struct_name}.");
|
118
|
+
| }
|
119
|
+
| // seek to the end
|
120
|
+
| if(fseek(#{klass.struct_name}_file,0,SEEK_END) == -1){
|
121
|
+
| rb_raise(rodException(),"Could not seek to end file for #{klass.struct_name}.");
|
122
|
+
| }
|
123
|
+
| // write empty data at the end
|
124
|
+
| char* #{klass.struct_name}_empty_data = calloc(page_size() * ALLOCATED_PAGES,1);
|
125
|
+
| if(write(model_p->#{klass.struct_name}_lib_file,#{klass.struct_name}_empty_data,
|
126
|
+
| page_size() * ALLOCATED_PAGES) == -1){
|
127
|
+
| rb_raise(rodException(),"Could not write to file for #{klass.struct_name}.");
|
128
|
+
| }
|
129
|
+
| // seek to the beginning
|
130
|
+
| if(fseek(#{klass.struct_name}_file,0,SEEK_SET) == -1){
|
131
|
+
| rb_raise(rodException(),"Could not seek to start file for #{klass.struct_name}.");
|
132
|
+
| }
|
133
|
+
| // mmap the extended file
|
134
|
+
| model_p->#{klass.struct_name}_table = mmap(NULL,
|
135
|
+
| model_p->#{klass.struct_name}_page_count * page_size(),
|
136
|
+
| PROT_WRITE | PROT_READ, MAP_SHARED, model_p->#{klass.struct_name}_lib_file,0);
|
137
|
+
| if(model_p->#{klass.struct_name}_table == MAP_FAILED){
|
138
|
+
| perror(NULL);
|
139
|
+
| rb_raise(rodException(),"Could not mmap segment for #{klass.struct_name}.");
|
140
|
+
| }
|
141
|
+
|#{update_pointer(klass) unless special_class?(klass)}
|
142
|
+
END
|
143
|
+
str.margin
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns true if the class is one of speciall classes
|
147
|
+
# (JoinElement, PolymorphicJoinElement, StringElement).
|
148
|
+
def special_class?(klass)
|
149
|
+
self.class.special_classes.include?(klass)
|
150
|
+
end
|
151
|
+
|
152
|
+
#########################################################################
|
153
|
+
# Implementations of abstract methods
|
154
|
+
#########################################################################
|
155
|
+
|
156
|
+
# Generates the code C responsible for management of the database.
|
157
|
+
def generate_c_code(path, classes)
|
158
|
+
if !@code_generated || @@rod_development_mode
|
159
|
+
self.class.inline(:C) do |builder|
|
160
|
+
builder.include '<stdlib.h>'
|
161
|
+
builder.include '<stdio.h>'
|
162
|
+
builder.include '<string.h>'
|
163
|
+
builder.include '<fcntl.h>'
|
164
|
+
builder.include '<unistd.h>'
|
165
|
+
builder.include '<errno.h>'
|
166
|
+
builder.include '<sys/mman.h>'
|
167
|
+
classes.each do |klass|
|
168
|
+
builder.prefix(klass.typedef_struct)
|
169
|
+
end
|
170
|
+
|
171
|
+
builder.prefix("const ALLOCATED_PAGES = 25;")
|
172
|
+
|
173
|
+
str =<<-END
|
174
|
+
|unsigned int page_size(){
|
175
|
+
| return sysconf(_SC_PAGE_SIZE);
|
176
|
+
|}
|
177
|
+
END
|
178
|
+
builder.prefix(str.margin)
|
179
|
+
|
180
|
+
str =<<-END
|
181
|
+
|VALUE rodException(){
|
182
|
+
| VALUE klass = rb_const_get(rb_cObject, rb_intern("Rod"));
|
183
|
+
| klass = rb_const_get(klass, rb_intern("DatabaseError"));
|
184
|
+
| return klass;
|
185
|
+
|}
|
186
|
+
END
|
187
|
+
builder.prefix(str.margin)
|
188
|
+
|
189
|
+
|
190
|
+
#########################################
|
191
|
+
# Model struct
|
192
|
+
#########################################
|
193
|
+
model_struct = model_struct_name(path);
|
194
|
+
str = <<-END
|
195
|
+
|typedef struct {\n
|
196
|
+
#{classes.map do |klass|
|
197
|
+
<<-SUBEND
|
198
|
+
| #{klass.struct_name} * #{klass.struct_name}_table;
|
199
|
+
| unsigned long #{klass.struct_name}_page_count;
|
200
|
+
| unsigned long #{klass.struct_name}_count;
|
201
|
+
| int #{klass.struct_name}_lib_file;
|
202
|
+
SUBEND
|
203
|
+
end.join("\n|\n")}
|
204
|
+
| // the path to the DB
|
205
|
+
| char * path;
|
206
|
+
|
|
207
|
+
|} #{model_struct};
|
208
|
+
END
|
209
|
+
builder.prefix(str.margin)
|
210
|
+
|
211
|
+
#########################################
|
212
|
+
# Join indices
|
213
|
+
#########################################
|
214
|
+
str =<<-END
|
215
|
+
|VALUE _join_element_index(unsigned long element_offset, unsigned long element_index, VALUE handler){
|
216
|
+
| #{model_struct} * model_p;
|
217
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
218
|
+
| return UINT2NUM((model_p->_join_element_table + element_offset + element_index)->offset);
|
219
|
+
|}
|
220
|
+
END
|
221
|
+
builder.c(str.margin)
|
222
|
+
|
223
|
+
str =<<-END
|
224
|
+
|VALUE _polymorphic_join_element_index(unsigned long element_offset,
|
225
|
+
| unsigned long element_index, VALUE handler){
|
226
|
+
| #{model_struct} * model_p;
|
227
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
228
|
+
| return UINT2NUM((model_p->_polymorphic_join_element_table +
|
229
|
+
| element_offset + element_index)->offset);
|
230
|
+
|}
|
231
|
+
END
|
232
|
+
builder.c(str.margin)
|
233
|
+
|
234
|
+
str =<<-END
|
235
|
+
|VALUE _polymorphic_join_element_class(unsigned long element_offset,
|
236
|
+
| unsigned long element_index, VALUE handler){
|
237
|
+
| #{model_struct} * model_p;
|
238
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
239
|
+
| return UINT2NUM((model_p->_polymorphic_join_element_table +
|
240
|
+
| element_offset + element_index)->class);
|
241
|
+
|}
|
242
|
+
END
|
243
|
+
builder.c(str.margin)
|
244
|
+
|
245
|
+
str =<<-END
|
246
|
+
|void _set_join_element_offset(unsigned long element_offset,
|
247
|
+
| unsigned long element_index, unsigned long offset,
|
248
|
+
| VALUE handler){
|
249
|
+
| #{model_struct} * model_p;
|
250
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
251
|
+
| _join_element * element_p;
|
252
|
+
| element_p = model_p->_join_element_table + element_offset + element_index;
|
253
|
+
| if(element_p->index != element_index){
|
254
|
+
| VALUE eClass = rb_const_get(rb_cObject, rb_intern("Exception"));
|
255
|
+
| rb_raise(eClass, "Join element indices are inconsistent: %lu %lu!",
|
256
|
+
| element_index, element_p->index);
|
257
|
+
| }
|
258
|
+
| element_p->offset = offset;
|
259
|
+
|}
|
260
|
+
END
|
261
|
+
builder.c(str.margin)
|
262
|
+
|
263
|
+
str =<<-END
|
264
|
+
|void _set_polymorphic_join_element_offset(unsigned long element_offset,
|
265
|
+
| unsigned long element_index, unsigned long offset, unsigned long class_id,
|
266
|
+
| VALUE handler){
|
267
|
+
| #{model_struct} * model_p;
|
268
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
269
|
+
| _polymorphic_join_element * element_p;
|
270
|
+
| element_p = model_p->_polymorphic_join_element_table + element_offset + element_index;
|
271
|
+
| if(element_p->index != element_index){
|
272
|
+
| VALUE eClass = rb_const_get(rb_cObject, rb_intern("Exception"));
|
273
|
+
| rb_raise(eClass, "Polymorphic join element indices are inconsistent: %lu %lu!",
|
274
|
+
| element_index, element_p->index);
|
275
|
+
| }
|
276
|
+
| element_p->offset = offset;
|
277
|
+
| element_p->class = class_id;
|
278
|
+
|}
|
279
|
+
END
|
280
|
+
builder.c(str.margin)
|
281
|
+
|
282
|
+
str =<<-END
|
283
|
+
|unsigned long _allocate_join_elements(VALUE size, VALUE handler){
|
284
|
+
| _join_element * element;
|
285
|
+
| unsigned long index;
|
286
|
+
| #{model_struct} * model_p;
|
287
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
288
|
+
| unsigned long result = model_p->_join_element_count;
|
289
|
+
| for(index = 0; index < size; index++){
|
290
|
+
| if(model_p->_join_element_count * sizeof(_join_element) >=
|
291
|
+
| page_size() * model_p->_join_element_page_count){
|
292
|
+
| \n#{mmap_class(JoinElement)}
|
293
|
+
| }
|
294
|
+
| element = model_p->_join_element_table + model_p->_join_element_count;
|
295
|
+
| model_p->_join_element_count++;
|
296
|
+
| element->index = index;
|
297
|
+
| element->offset = 0;
|
298
|
+
| }
|
299
|
+
| return result;
|
300
|
+
|}
|
301
|
+
END
|
302
|
+
builder.c(str.margin)
|
303
|
+
|
304
|
+
str =<<-END
|
305
|
+
|unsigned long _allocate_polymorphic_join_elements(VALUE size, VALUE handler){
|
306
|
+
| _polymorphic_join_element * element;
|
307
|
+
| unsigned long index;
|
308
|
+
| #{model_struct} * model_p;
|
309
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
310
|
+
| unsigned long result = model_p->_polymorphic_join_element_count;
|
311
|
+
| for(index = 0; index < size; index++){
|
312
|
+
| if(model_p->_polymorphic_join_element_count *
|
313
|
+
| sizeof(_polymorphic_join_element) >=
|
314
|
+
| page_size() * model_p->_polymorphic_join_element_page_count){
|
315
|
+
| \n#{mmap_class(PolymorphicJoinElement)}
|
316
|
+
| }
|
317
|
+
| element = model_p->_polymorphic_join_element_table +
|
318
|
+
| model_p->_polymorphic_join_element_count;
|
319
|
+
| model_p->_polymorphic_join_element_count++;
|
320
|
+
| element->index = index;
|
321
|
+
| element->offset = 0;
|
322
|
+
| element->class = 0;
|
323
|
+
| }
|
324
|
+
| return result;
|
325
|
+
|}
|
326
|
+
END
|
327
|
+
builder.c(str.margin)
|
328
|
+
|
329
|
+
#########################################
|
330
|
+
# Strings
|
331
|
+
#########################################
|
332
|
+
str =<<-END
|
333
|
+
|VALUE _read_string(unsigned long length, unsigned long offset, VALUE handler){
|
334
|
+
| #{model_struct} * model_p;
|
335
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
336
|
+
| char * str = model_p->#{StringElement.struct_name}_table + offset;
|
337
|
+
| return rb_str_new(str, length);
|
338
|
+
|}
|
339
|
+
END
|
340
|
+
builder.c(str.margin)
|
341
|
+
|
342
|
+
str =<<-END
|
343
|
+
|// The first argument is the string to be stored.
|
344
|
+
|// The return value is a pair: length and offset.
|
345
|
+
|VALUE _set_string(VALUE ruby_value, VALUE handler){
|
346
|
+
| #{model_struct} * model_p;
|
347
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
348
|
+
| unsigned long length = RSTRING_LEN(ruby_value);
|
349
|
+
| char * value = RSTRING_PTR(ruby_value);
|
350
|
+
| unsigned long string_offset, page_offset, current_page;
|
351
|
+
| char * dest;
|
352
|
+
| // table:
|
353
|
+
| // - address of the first page
|
354
|
+
| // page_count:
|
355
|
+
| // - during write - number of allocated pages
|
356
|
+
| // count:
|
357
|
+
| // - total number of bytes
|
358
|
+
| long length_left = length;
|
359
|
+
| // first free byte in current page
|
360
|
+
| string_offset = model_p->#{StringElement.struct_name}_count % page_size();
|
361
|
+
| page_offset = model_p->#{StringElement.struct_name}_count / page_size();
|
362
|
+
| current_page = page_offset;
|
363
|
+
| while(length_left > 0){
|
364
|
+
| if(length_left + string_offset >= page_size()){
|
365
|
+
| \n#{mmap_class(StringElement)}
|
366
|
+
| }
|
367
|
+
| dest = model_p->#{StringElement.struct_name}_table +
|
368
|
+
| current_page * page_size() + string_offset;
|
369
|
+
| if(length_left > page_size()){
|
370
|
+
| memcpy(dest,value,page_size());
|
371
|
+
| } else {
|
372
|
+
| memcpy(dest,value,length_left);
|
373
|
+
| }
|
374
|
+
| value += page_size();
|
375
|
+
| current_page++;
|
376
|
+
| length_left -= page_size();
|
377
|
+
| }
|
378
|
+
|
|
379
|
+
| model_p->#{StringElement.struct_name}_count += length;
|
380
|
+
|
|
381
|
+
| VALUE result = rb_ary_new();
|
382
|
+
| rb_ary_push(result, UINT2NUM(length));
|
383
|
+
| rb_ary_push(result, UINT2NUM(string_offset + page_offset * page_size()));
|
384
|
+
| return result;
|
385
|
+
|}
|
386
|
+
END
|
387
|
+
builder.c(str.margin)
|
388
|
+
|
389
|
+
#########################################
|
390
|
+
# Object accessors
|
391
|
+
#########################################
|
392
|
+
classes.each do |klass|
|
393
|
+
self.class.field_reader("#{klass.struct_name}_count",
|
394
|
+
"unsigned long",builder,model_struct)
|
395
|
+
self.class.field_writer("#{klass.struct_name}_count",
|
396
|
+
"unsigned long",builder,model_struct)
|
397
|
+
self.class.field_reader("#{klass.struct_name}_page_count",
|
398
|
+
"unsigned long",builder,model_struct)
|
399
|
+
self.class.field_writer("#{klass.struct_name}_page_count",
|
400
|
+
"unsigned long",builder,model_struct)
|
401
|
+
end
|
402
|
+
|
403
|
+
#########################################
|
404
|
+
# Storage
|
405
|
+
#########################################
|
406
|
+
classes.each do |klass|
|
407
|
+
next if special_class?(klass)
|
408
|
+
str =<<-END
|
409
|
+
|// Store the object in the database.
|
410
|
+
|void _store_#{klass.struct_name}(VALUE object, VALUE handler){
|
411
|
+
|
|
412
|
+
| #{model_struct} * model_p;
|
413
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
414
|
+
| if((model_p->#{klass.struct_name}_count+1) * sizeof(#{klass.struct_name}) >=
|
415
|
+
| model_p->#{klass.struct_name}_page_count * page_size()){
|
416
|
+
| \n#{mmap_class(klass)}
|
417
|
+
| }
|
418
|
+
| #{klass.struct_name} * struct_p = model_p->#{klass.struct_name}_table +
|
419
|
+
| model_p->#{klass.struct_name}_count;
|
420
|
+
| //printf("struct assigned\\n");
|
421
|
+
| model_p->#{klass.struct_name}_count++;
|
422
|
+
|
|
423
|
+
| //the number is incresed by 1, because 0 indicates that the
|
424
|
+
| //(referenced) object is nil
|
425
|
+
| struct_p->rod_id = model_p->#{klass.struct_name}_count;
|
426
|
+
| rb_iv_set(object, \"@rod_id\",UINT2NUM(struct_p->rod_id));
|
427
|
+
|}
|
428
|
+
END
|
429
|
+
builder.c(str.margin)
|
430
|
+
end
|
431
|
+
|
432
|
+
#########################################
|
433
|
+
# init handler
|
434
|
+
#########################################
|
435
|
+
str = <<-END
|
436
|
+
|VALUE _init_handler(char * dir_path){
|
437
|
+
| #{model_struct} * model_p;
|
438
|
+
| model_p = ALLOC(#{model_struct});
|
439
|
+
|
|
440
|
+
| #{init_structs(classes)}
|
441
|
+
|
|
442
|
+
| // set dir path
|
443
|
+
| model_p->path = malloc(sizeof(char)*(strlen(dir_path)+1));
|
444
|
+
| strcpy(model_p->path,dir_path);
|
445
|
+
|
|
446
|
+
| //create the wrapping object
|
447
|
+
| VALUE cClass = rb_define_class("#{model_struct_name(path).camelcase(true)}",
|
448
|
+
| rb_cObject);
|
449
|
+
| return Data_Wrap_Struct(cClass, 0, free, model_p);
|
450
|
+
|}
|
451
|
+
END
|
452
|
+
builder.c(str.margin)
|
453
|
+
|
454
|
+
#########################################
|
455
|
+
# create
|
456
|
+
#########################################
|
457
|
+
str = <<-END
|
458
|
+
|void _create(VALUE handler){
|
459
|
+
| #{model_struct} * model_p;
|
460
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
461
|
+
|
|
462
|
+
| //mmap the structures
|
463
|
+
| \n#{classes.map{|klass| mmap_class(klass)}.join("\n|\n")}
|
464
|
+
|}
|
465
|
+
END
|
466
|
+
builder.c(str.margin)
|
467
|
+
|
468
|
+
#########################################
|
469
|
+
# open
|
470
|
+
#########################################
|
471
|
+
str =<<-END
|
472
|
+
|void _open(VALUE handler){
|
473
|
+
| #{model_struct} * model_p;
|
474
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
475
|
+
|
|
476
|
+
| \n#{classes.map do |klass|
|
477
|
+
<<-SUBEND
|
478
|
+
| model_p->#{klass.struct_name}_lib_file = -1;
|
479
|
+
|
|
480
|
+
| if(model_p->#{klass.struct_name}_page_count > 0){
|
481
|
+
| \n#{open_class_file(klass)}
|
482
|
+
| if((model_p->#{klass.struct_name}_table = mmap(NULL,
|
483
|
+
| model_p->#{klass.struct_name}_page_count * page_size(), PROT_WRITE | PROT_READ,
|
484
|
+
| MAP_SHARED, model_p->#{klass.struct_name}_lib_file, 0)) == MAP_FAILED){
|
485
|
+
| perror(NULL);
|
486
|
+
| rb_raise(rodException(),"Could not mmap class '#{klass}'.");
|
487
|
+
| }
|
488
|
+
| #{update_pointer(klass) unless special_class?(klass)}
|
489
|
+
| } else {
|
490
|
+
| model_p->#{klass.struct_name}_table = NULL;
|
491
|
+
| }
|
492
|
+
SUBEND
|
493
|
+
end.join("\n")}
|
494
|
+
|}
|
495
|
+
END
|
496
|
+
builder.c(str.margin)
|
497
|
+
|
498
|
+
#########################################
|
499
|
+
# close
|
500
|
+
#########################################
|
501
|
+
str =<<-END
|
502
|
+
|// if +classes+ are Qnil, the DB was open in readonly mode.
|
503
|
+
|void _close(VALUE handler){
|
504
|
+
| #{model_struct} * model_p;
|
505
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
506
|
+
|
|
507
|
+
| \n#{classes.map do |klass|
|
508
|
+
<<-SUBEND
|
509
|
+
| if(model_p->#{klass.struct_name}_table != NULL){
|
510
|
+
| if(munmap(model_p->#{klass.struct_name}_table,
|
511
|
+
| page_size() * model_p->#{klass.struct_name}_page_count) == -1){
|
512
|
+
| rb_raise(rodException(),"Could not unmap #{klass.struct_name}.");
|
513
|
+
| }
|
514
|
+
| }
|
515
|
+
| if(close(model_p->#{klass.struct_name}_lib_file) == -1){
|
516
|
+
| rb_raise(rodException(),"Could not close model file for #{klass}.");
|
517
|
+
| }
|
518
|
+
SUBEND
|
519
|
+
end.join("\n")}
|
520
|
+
|}
|
521
|
+
END
|
522
|
+
builder.c(str.margin)
|
523
|
+
|
524
|
+
|
525
|
+
#########################################
|
526
|
+
# Utilities
|
527
|
+
#########################################
|
528
|
+
str = <<-END
|
529
|
+
|void _print_layout(VALUE handler){
|
530
|
+
| #{model_struct} * model_p;
|
531
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
532
|
+
| printf("============= Data layout START =============\\n");
|
533
|
+
| \n#{classes.map do |klass|
|
534
|
+
str =<<-SUBEND
|
535
|
+
| printf("-- #{klass} --\\n");
|
536
|
+
| printf("Size of #{klass.struct_name} %lu\\n",(unsigned long)sizeof(#{klass.struct_name}));
|
537
|
+
| \n#{klass.layout}
|
538
|
+
| printf("Page count: %lu, count %lu, pointer: %lx\\n",
|
539
|
+
| model_p->#{klass.struct_name}_page_count,
|
540
|
+
| model_p->#{klass.struct_name}_count,
|
541
|
+
| (unsigned long)model_p->#{klass.struct_name}_table);
|
542
|
+
SUBEND
|
543
|
+
str.margin
|
544
|
+
end.join("\n")}
|
545
|
+
| printf("============= Data layout END =============\\n");
|
546
|
+
|}
|
547
|
+
END
|
548
|
+
builder.c(str.margin)
|
549
|
+
|
550
|
+
str =<<-END
|
551
|
+
|void _print_system_error(){
|
552
|
+
| perror(NULL);
|
553
|
+
|}
|
554
|
+
END
|
555
|
+
builder.c(str.margin)
|
556
|
+
|
557
|
+
str =<<-END
|
558
|
+
|unsigned int _page_size(){
|
559
|
+
| return page_size();
|
560
|
+
|}
|
561
|
+
END
|
562
|
+
builder.c(str.margin)
|
563
|
+
|
564
|
+
if @@rod_development_mode
|
565
|
+
# This method is created to force rebuild of the C code, since
|
566
|
+
# it is rebuild on the basis of methods' signatures change.
|
567
|
+
builder.c_singleton("void __unused_method_#{rand(1000)}(){}")
|
568
|
+
end
|
569
|
+
end
|
570
|
+
@code_generated = true
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
# Reads the value of a specified field of the C-structure.
|
575
|
+
def self.field_reader(name,result_type,builder,model_struct)
|
576
|
+
str =<<-END
|
577
|
+
|#{result_type} _#{name}(VALUE handler){
|
578
|
+
| #{model_struct} * model_p;
|
579
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
580
|
+
| return model_p->#{name};
|
581
|
+
|}
|
582
|
+
END
|
583
|
+
builder.c(str.margin)
|
584
|
+
end
|
585
|
+
|
586
|
+
# Writes the value of a specified field of the C-structure.
|
587
|
+
def self.field_writer(name,arg_type,builder,model_struct)
|
588
|
+
str =<<-END
|
589
|
+
|void _#{name}_equals(VALUE handler,#{arg_type} value){
|
590
|
+
| #{model_struct} * model_p;
|
591
|
+
| Data_Get_Struct(handler,#{model_struct},model_p);
|
592
|
+
| model_p->#{name} = value;
|
593
|
+
|}
|
594
|
+
END
|
595
|
+
builder.c(str.margin)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|