rod 0.6.0
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/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
|