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.
@@ -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
@@ -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