mmap 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/Changes ADDED
@@ -0,0 +1,55 @@
1
+
2
+ -- 0.1.2
3
+
4
+ * fixed some bugs (options & new) (Thanks Joseph McDonald <joe@vpop.net>)
5
+ * added anonymous map
6
+ * added mlock, munlock, etc
7
+
8
+ -- 0.1.3
9
+
10
+ * added #count, #slice, #slice!
11
+ * added #insert, #casecmp (>= 171)
12
+ * corrected NEW2LONG
13
+
14
+ -- 0.1.4
15
+
16
+ * added #lstrip!, #rstrip! for 1.7.1
17
+ * corrected strip!
18
+ * corrected mm_bang_i (reverse!, etc)
19
+ * added a small test (make test)
20
+
21
+ -- 0.1.5
22
+
23
+ * added str[/a(a)/, 1] for 1.7.1
24
+ * corrected mm_aset (mm_update(str, ...))
25
+ * corrected ==, ===, eql?
26
+ * corrected mm_sub_bang, mm_gsub_bang (to_str)
27
+
28
+ -- 0.1.6
29
+
30
+ * adapted for 1.7.2 (mm_str)
31
+ * corrected real for mm_sub_bang
32
+ * protected against old class
33
+
34
+ -- 0.1.7
35
+
36
+ * 1.7.2 (::allocate, #initialize)
37
+ * added "a" for ::new
38
+ * experimental EXP_INCR_SIZE (4096) ("increment" => 4096)
39
+ * tests for RUNIT/Test::Unit
40
+
41
+ -- 0.1.8
42
+
43
+ * test for madvise(2)
44
+ * don't test size for MAP_ANON
45
+ * added syntax Mmap(nil, length)
46
+ * documentation : make rd2; make rdoc
47
+
48
+ -- 0.1.9
49
+
50
+ * String#slice! was modified in 1.6.8
51
+ * added ::new(IO)
52
+
53
+ --- 0.2.0
54
+
55
+ * adapted for 1.8.0
@@ -0,0 +1,11 @@
1
+ Changes
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ b.rb
6
+ ext/mmap/extconf.rb
7
+ ext/mmap/mmap.c
8
+ lib/mmap.rb
9
+ mmap.html
10
+ mmap.rd
11
+ test/test_mmap.rb
@@ -0,0 +1,36 @@
1
+ = Mmap
2
+
3
+ * http://rubyforge.org/frs/?group_id=8350
4
+
5
+ == DESCRIPTION
6
+
7
+ The Mmap class implement memory-mapped file objects
8
+
9
+ == SYNOPSIS
10
+
11
+ require 'mmap'
12
+
13
+ mmap = Mmap.new(__FILE__)
14
+ mmap.advise(Mmap::MADV_SEQUENTIAL)
15
+
16
+ mmap.each do |line|
17
+ puts line
18
+ end
19
+
20
+ == Installation
21
+
22
+ gem install mmap
23
+
24
+ == Documentation
25
+
26
+ rake docs
27
+
28
+ == Copying
29
+
30
+ This extension module is copyrighted free software by Guy Decoux
31
+
32
+ You can redistribute it and/or modify it under the same term as
33
+ Ruby.
34
+
35
+
36
+ Guy Decoux <ts@moulon.inra.fr>
@@ -0,0 +1,27 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ gem 'rake-compiler', '>= 0.4.1'
7
+ require "rake/extensiontask"
8
+
9
+ HOE = Hoe.spec 'mmap' do
10
+ developer('Guy Decoux', 'ts@moulon.inra.fr')
11
+ self.readme_file = 'README.rdoc'
12
+ self.history_file = 'Changes'
13
+ self.extra_rdoc_files = FileList['*.rdoc']
14
+
15
+ %w{ rake-compiler }.each do |dep|
16
+ self.extra_dev_deps << [dep, '>= 0']
17
+ end
18
+
19
+ self.spec_extras = { :extensions => ["ext/mmap/extconf.rb"] }
20
+ end
21
+
22
+ RET = Rake::ExtensionTask.new("mmap", HOE.spec) do |ext|
23
+ ext.lib_dir = File.join('lib', 'mmap')
24
+ end
25
+
26
+ # vim: syntax=ruby
27
+
data/b.rb ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/ruby
2
+ $LOAD_PATH.unshift "."
3
+ require "mmap"
4
+ PAGESIZE = 4096
5
+ f = File.open("aa", "w")
6
+ f.write("\0" * PAGESIZE)
7
+ f.write("b.rb")
8
+ f.write("\0" * PAGESIZE)
9
+ f.close
10
+ m = Mmap.new("aa", "w", "offset" => 0)
11
+ p m.size == "b.rb".size + 2 * PAGESIZE
12
+ p m.scan(/[a-z.]+/) == ["b.rb"]
13
+ p m.index("b.rb") == PAGESIZE
14
+ p m.rindex("b.rb") == PAGESIZE
15
+ p m.sub!(/[a-z.]+/, "toto") == m
16
+ p m.scan(/[a-z.]+/) == ["toto"]
17
+ begin
18
+ m.sub!(/[a-z.]+/, "alpha")
19
+ puts "not OK must give an error"
20
+ rescue
21
+ puts "OK : #$!"
22
+ end
23
+ m.munmap
24
+ m = Mmap.new("aa", "rw")
25
+ p m.index("toto") == PAGESIZE
26
+ p m.sub!(/([a-z.]+)/, "alpha") == m
27
+ p $& == "toto"
28
+ p $1 == "toto"
29
+ p m.index("toto") == nil
30
+ p m.index("alpha") == PAGESIZE
31
+ p m.size == 5 + 2 * PAGESIZE
32
+ m.gsub!(/\0/, "X")
33
+ p m.size == 5 + 2 * PAGESIZE
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+ ARGV.collect! {|x| x.sub(/^--with-mmap-prefix=/, "--with-mmap-dir=") }
3
+
4
+ require 'mkmf'
5
+
6
+ if unknown = enable_config("unknown")
7
+ libs = if CONFIG.key?("LIBRUBYARG_STATIC")
8
+ Config::expand(CONFIG["LIBRUBYARG_STATIC"].dup).sub(/^-l/, '')
9
+ else
10
+ Config::expand(CONFIG["LIBRUBYARG"].dup).sub(/^lib([^.]*).*/, '\\1')
11
+ end
12
+ unknown = find_library(libs, "ruby_init",
13
+ Config::expand(CONFIG["archdir"].dup))
14
+ end
15
+
16
+ dir_config("mmap")
17
+
18
+ ["lstrip", "match", "insert", "casecmp"].each do |func|
19
+ if "aa".respond_to?(func)
20
+ $CFLAGS += " -DHAVE_RB_STR_#{func.upcase}"
21
+ end
22
+ end
23
+
24
+ if enable_config("ipc")
25
+ unless have_func("semctl") && have_func("shmctl")
26
+ $stderr.puts "\tIPC will not be available"
27
+ end
28
+ end
29
+
30
+ $CFLAGS += " -DRUBYLIBDIR='\"#{CONFIG['rubylibdir']}\"'"
31
+
32
+ create_makefile "mmap"
@@ -0,0 +1,2624 @@
1
+ #include <ruby.h>
2
+ #include <fcntl.h>
3
+ #include <ctype.h>
4
+ #include <sys/types.h>
5
+ #include <sys/stat.h>
6
+ #include <unistd.h>
7
+ #include <sys/mman.h>
8
+
9
+ #if HAVE_SEMCTL && HAVE_SHMCTL
10
+ #include <sys/shm.h>
11
+ #include <sys/ipc.h>
12
+ #include <sys/sem.h>
13
+ #endif
14
+
15
+ #include <rubyio.h>
16
+ #include <intern.h>
17
+ #include <re.h>
18
+
19
+ #ifndef StringValue
20
+ #define StringValue(x) do { \
21
+ if (TYPE(x) != T_STRING) x = rb_str_to_str(x); \
22
+ } while (0)
23
+ #endif
24
+
25
+ #ifndef StringValuePtr
26
+ #define StringValuePtr(x) STR2CSTR(x)
27
+ #endif
28
+
29
+ #ifndef SafeStringValue
30
+ #define SafeStringValue(x) Check_SafeStr(x)
31
+ #endif
32
+
33
+ #ifndef MADV_NORMAL
34
+ #ifdef POSIX_MADV_NORMAL
35
+ #define MADV_NORMAL POSIX_MADV_NORMAL
36
+ #define MADV_RANDOM POSIX_MADV_RANDOM
37
+ #define MADV_SEQUENTIAL POSIX_MADV_SEQUENTIAL
38
+ #define MADV_WILLNEED POSIX_MADV_WILLNEED
39
+ #define MADV_DONTNEED POSIX_MADV_DONTNEED
40
+ #define madvise posix_madvise
41
+ #endif
42
+ #endif
43
+
44
+ #define BEG(no) regs->beg[no]
45
+ #define END(no) regs->end[no]
46
+
47
+ #ifndef MMAP_RETTYPE
48
+ #ifndef _POSIX_C_SOURCE
49
+ #define _POSIX_C_SOURCE 199309
50
+ #endif /* !_POSIX_C_SOURCE */
51
+ #ifdef _POSIX_VERSION
52
+ #if _POSIX_VERSION >= 199309
53
+ #define MMAP_RETTYPE void *
54
+ #endif /* _POSIX_VERSION >= 199309 */
55
+ #endif /* _POSIX_VERSION */
56
+ #endif /* !MMAP_RETTYPE */
57
+
58
+ #ifndef MMAP_RETTYPE
59
+ #define MMAP_RETTYPE caddr_t
60
+ #endif
61
+
62
+ #ifndef MAP_FAILED
63
+ #define MAP_FAILED ((caddr_t)-1)
64
+ #endif /* !MAP_FAILED */
65
+
66
+ #ifndef MAP_ANON
67
+ #ifdef MAP_ANONYMOUS
68
+ #define MAP_ANON MAP_ANONYMOUS
69
+ #endif
70
+ #endif
71
+
72
+ static VALUE mm_cMap;
73
+
74
+ #define EXP_INCR_SIZE 4096
75
+
76
+ typedef struct {
77
+ MMAP_RETTYPE addr;
78
+ int smode, pmode, vscope;
79
+ int advice, flag;
80
+ VALUE key;
81
+ int semid, shmid;
82
+ size_t len, real, incr;
83
+ off_t offset;
84
+ char *path, *template;
85
+ } mm_mmap;
86
+
87
+ typedef struct {
88
+ int count;
89
+ mm_mmap *t;
90
+ } mm_ipc;
91
+
92
+ typedef struct {
93
+ VALUE obj, *argv;
94
+ int flag, id, argc;
95
+ } mm_bang;
96
+
97
+ #define MM_MODIFY 1
98
+ #define MM_ORIGIN 2
99
+ #define MM_CHANGE (MM_MODIFY | 4)
100
+ #define MM_PROTECT 8
101
+
102
+ #define MM_FROZEN (1<<0)
103
+ #define MM_FIXED (1<<1)
104
+ #define MM_ANON (1<<2)
105
+ #define MM_LOCK (1<<3)
106
+ #define MM_IPC (1<<4)
107
+ #define MM_TMP (1<<5)
108
+
109
+ #if HAVE_SEMCTL && HAVE_SHMCTL
110
+ static char template[1024];
111
+
112
+ union semun
113
+ {
114
+ int val;
115
+ struct semid_ds *buf;
116
+ unsigned short int *array;
117
+ struct seminfo *__buf;
118
+ };
119
+ #endif
120
+
121
+ static void
122
+ mm_free(i_mm)
123
+ mm_ipc *i_mm;
124
+ {
125
+ #if HAVE_SEMCTL && HAVE_SHMCTL
126
+ if (i_mm->t->flag & MM_IPC) {
127
+ struct shmid_ds buf;
128
+
129
+ if (shmctl(i_mm->t->shmid, IPC_STAT, &buf) != -1) {
130
+ if (buf.shm_nattch == 1 && (i_mm->t->flag & MM_TMP)) {
131
+ semctl(i_mm->t->semid, 0, IPC_RMID);
132
+ if (i_mm->t->template) {
133
+ unlink(i_mm->t->template);
134
+ free(i_mm->t->template);
135
+ }
136
+ }
137
+ }
138
+ shmdt(i_mm->t);
139
+ }
140
+ else {
141
+ free(i_mm->t);
142
+ }
143
+ #endif
144
+ if (i_mm->t->path) {
145
+ munmap(i_mm->t->addr, i_mm->t->len);
146
+ if (i_mm->t->path != (char *)-1) {
147
+ if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE &&
148
+ truncate(i_mm->t->path, i_mm->t->real) == -1) {
149
+ free(i_mm->t->path);
150
+ free(i_mm);
151
+ rb_raise(rb_eTypeError, "truncate");
152
+ }
153
+ free(i_mm->t->path);
154
+ }
155
+ }
156
+ free(i_mm);
157
+ }
158
+
159
+ static void
160
+ mm_lock(i_mm, wait_lock)
161
+ mm_ipc *i_mm;
162
+ int wait_lock;
163
+ {
164
+ #if HAVE_SEMCTL && HAVE_SHMCTL
165
+ struct sembuf sem_op;
166
+
167
+ if (i_mm->t->flag & MM_IPC) {
168
+ i_mm->count++;
169
+ if (i_mm->count == 1) {
170
+ retry:
171
+ sem_op.sem_num = 0;
172
+ sem_op.sem_op = -1;
173
+ sem_op.sem_flg = IPC_NOWAIT;
174
+ if (semop(i_mm->t->semid, &sem_op, 1) == -1) {
175
+ if (errno == EAGAIN) {
176
+ if (!wait_lock) {
177
+ rb_raise(rb_const_get(rb_mErrno, rb_intern("EAGAIN")), "EAGAIN");
178
+ }
179
+ rb_thread_sleep(1);
180
+ goto retry;
181
+ }
182
+ rb_sys_fail("semop()");
183
+ }
184
+ }
185
+ }
186
+ #endif
187
+ }
188
+
189
+ static void
190
+ mm_unlock(i_mm)
191
+ mm_ipc *i_mm;
192
+ {
193
+ #if HAVE_SEMCTL && HAVE_SHMCTL
194
+ struct sembuf sem_op;
195
+
196
+ if (i_mm->t->flag & MM_IPC) {
197
+ i_mm->count--;
198
+ if (!i_mm->count) {
199
+ retry:
200
+ sem_op.sem_num = 0;
201
+ sem_op.sem_op = 1;
202
+ sem_op.sem_flg = IPC_NOWAIT;
203
+ if (semop(i_mm->t->semid, &sem_op, 1) == -1) {
204
+ if (errno == EAGAIN) {
205
+ rb_thread_sleep(1);
206
+ goto retry;
207
+ }
208
+ rb_sys_fail("semop()");
209
+ }
210
+ }
211
+ }
212
+ #endif
213
+ }
214
+
215
+ #define GetMmap(obj, i_mm, t_modify) \
216
+ Data_Get_Struct(obj, mm_ipc, i_mm); \
217
+ if (!i_mm->t->path) { \
218
+ rb_raise(rb_eIOError, "unmapped file"); \
219
+ } \
220
+ if ((t_modify & MM_MODIFY) && (i_mm->t->flag & MM_FROZEN)) { \
221
+ rb_error_frozen("mmap"); \
222
+ }
223
+
224
+ static VALUE
225
+ mm_vunlock(obj)
226
+ VALUE obj;
227
+ {
228
+ mm_ipc *i_mm;
229
+
230
+ GetMmap(obj, i_mm, 0);
231
+ mm_unlock(i_mm);
232
+ return Qnil;
233
+ }
234
+
235
+ /*
236
+ * call-seq: semlock
237
+ *
238
+ * Create a lock
239
+ */
240
+ static VALUE
241
+ mm_semlock(argc, argv, obj)
242
+ int argc;
243
+ VALUE *argv, obj;
244
+ {
245
+ mm_ipc *i_mm;
246
+
247
+ GetMmap(obj, i_mm, 0);
248
+ if (!(i_mm->t->flag & MM_IPC)) {
249
+ rb_warning("useless use of #semlock");
250
+ rb_yield(obj);
251
+ }
252
+ else {
253
+ #if HAVE_SEMCTL && HAVE_SHMCTL
254
+ VALUE a;
255
+ int wait_lock = Qtrue;
256
+
257
+ if (rb_scan_args(argc, argv, "01", &a)) {
258
+ wait_lock = RTEST(a);
259
+ }
260
+ mm_lock(i_mm, wait_lock);
261
+ rb_ensure(rb_yield, obj, mm_vunlock, obj);
262
+ #endif
263
+ }
264
+ return Qnil;
265
+ }
266
+
267
+ /*
268
+ * call-seq: ipc_key
269
+ *
270
+ * Get the ipc key
271
+ */
272
+ static VALUE
273
+ mm_ipc_key(obj)
274
+ VALUE obj;
275
+ {
276
+ mm_ipc *i_mm;
277
+
278
+ GetMmap(obj, i_mm, 0);
279
+ if (i_mm->t->flag & MM_IPC) {
280
+ return INT2NUM(i_mm->t->key);
281
+ }
282
+ return INT2NUM(-1);
283
+ }
284
+
285
+ /*
286
+ * Document-method: munmap
287
+ * Document-method: unmap
288
+ *
289
+ * call-seq: munmap
290
+ *
291
+ * terminate the association
292
+ */
293
+ static VALUE
294
+ mm_unmap(obj)
295
+ VALUE obj;
296
+ {
297
+ mm_ipc *i_mm;
298
+
299
+ GetMmap(obj, i_mm, 0);
300
+ if (i_mm->t->path) {
301
+ mm_lock(i_mm, Qtrue);
302
+ munmap(i_mm->t->addr, i_mm->t->len);
303
+ if (i_mm->t->path != (char *)-1) {
304
+ if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE &&
305
+ truncate(i_mm->t->path, i_mm->t->real) == -1) {
306
+ rb_raise(rb_eTypeError, "truncate");
307
+ }
308
+ free(i_mm->t->path);
309
+ }
310
+ i_mm->t->path = '\0';
311
+ mm_unlock(i_mm);
312
+ }
313
+ return Qnil;
314
+ }
315
+
316
+ /*
317
+ * call-seq: freeze
318
+ *
319
+ * freeze the current file
320
+ */
321
+ static VALUE
322
+ mm_freeze(obj)
323
+ VALUE obj;
324
+ {
325
+ mm_ipc *i_mm;
326
+ rb_obj_freeze(obj);
327
+ GetMmap(obj, i_mm, 0);
328
+ i_mm->t->flag |= MM_FROZEN;
329
+ return obj;
330
+ }
331
+
332
+ static VALUE
333
+ mm_str(obj, modify)
334
+ VALUE obj;
335
+ int modify;
336
+ {
337
+ mm_ipc *i_mm;
338
+ VALUE ret = Qnil;
339
+
340
+ GetMmap(obj, i_mm, modify & ~MM_ORIGIN);
341
+ if (modify & MM_MODIFY) {
342
+ if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap");
343
+ if (!OBJ_TAINTED(ret) && rb_safe_level() >= 4)
344
+ rb_raise(rb_eSecurityError, "Insecure: can't modify mmap");
345
+ }
346
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
347
+ ret = rb_obj_alloc(rb_cString);
348
+ if (rb_obj_tainted(obj)) {
349
+ OBJ_TAINT(ret);
350
+ }
351
+ #else
352
+ if (rb_obj_tainted(obj)) {
353
+ ret = rb_tainted_str_new2("");
354
+ }
355
+ else {
356
+ ret = rb_str_new2("");
357
+ }
358
+ free(RSTRING(ret)->ptr);
359
+ #endif
360
+ RSTRING(ret)->ptr = i_mm->t->addr;
361
+ RSTRING(ret)->len = i_mm->t->real;
362
+ if (modify & MM_ORIGIN) {
363
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
364
+ RSTRING(ret)->aux.shared = ret;
365
+ FL_SET(ret, ELTS_SHARED);
366
+ #else
367
+ RSTRING(ret)->orig = ret;
368
+ #endif
369
+ }
370
+ if (i_mm->t->flag & MM_FROZEN) {
371
+ ret = rb_obj_freeze(ret);
372
+ }
373
+ return ret;
374
+ }
375
+
376
+ /*
377
+ * call-seq: to_str
378
+ *
379
+ * Convert object to a string
380
+ */
381
+ static VALUE
382
+ mm_to_str(obj)
383
+ VALUE obj;
384
+ {
385
+ return mm_str(obj, MM_ORIGIN);
386
+ }
387
+
388
+ extern char *ruby_strdup();
389
+
390
+ typedef struct {
391
+ mm_ipc *i_mm;
392
+ size_t len;
393
+ } mm_st;
394
+
395
+ static VALUE
396
+ mm_i_expand(st_mm)
397
+ mm_st *st_mm;
398
+ {
399
+ int fd;
400
+ mm_ipc *i_mm = st_mm->i_mm;
401
+ size_t len = st_mm->len;
402
+
403
+ if (munmap(i_mm->t->addr, i_mm->t->len)) {
404
+ rb_raise(rb_eArgError, "munmap failed");
405
+ }
406
+ if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) {
407
+ rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path);
408
+ }
409
+ if (len > i_mm->t->len) {
410
+ if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) {
411
+ rb_raise(rb_eIOError, "Can't lseek %d", len - i_mm->t->len - 1);
412
+ }
413
+ if (write(fd, "\000", 1) != 1) {
414
+ rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path);
415
+ }
416
+ }
417
+ else if (len < i_mm->t->len && truncate(i_mm->t->path, len) == -1) {
418
+ rb_raise(rb_eIOError, "Can't truncate %s", i_mm->t->path);
419
+ }
420
+ i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset);
421
+ close(fd);
422
+ if (i_mm->t->addr == MAP_FAILED) {
423
+ rb_raise(rb_eArgError, "mmap failed");
424
+ }
425
+ #ifdef MADV_NORMAL
426
+ if (i_mm->t->advice && madvise(i_mm->t->addr, len, i_mm->t->advice) == -1) {
427
+ rb_raise(rb_eArgError, "madvise(%d)", errno);
428
+ }
429
+ #endif
430
+ if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) {
431
+ rb_raise(rb_eArgError, "mlock(%d)", errno);
432
+ }
433
+ i_mm->t->len = len;
434
+ return Qnil;
435
+ }
436
+
437
+ static void
438
+ mm_expandf(i_mm, len)
439
+ mm_ipc *i_mm;
440
+ size_t len;
441
+ {
442
+ int status;
443
+ mm_st st_mm;
444
+
445
+ if (i_mm->t->vscope == MAP_PRIVATE) {
446
+ rb_raise(rb_eTypeError, "expand for a private map");
447
+ }
448
+ if (i_mm->t->flag & MM_FIXED) {
449
+ rb_raise(rb_eTypeError, "expand for a fixed map");
450
+ }
451
+ if (!i_mm->t->path || i_mm->t->path == (char *)-1) {
452
+ rb_raise(rb_eTypeError, "expand for an anonymous map");
453
+ }
454
+ st_mm.i_mm = i_mm;
455
+ st_mm.len = len;
456
+ if (i_mm->t->flag & MM_IPC) {
457
+ mm_lock(i_mm, Qtrue);
458
+ rb_protect(mm_i_expand, (VALUE)&st_mm, &status);
459
+ mm_unlock(i_mm);
460
+ if (status) {
461
+ rb_jump_tag(status);
462
+ }
463
+ }
464
+ else {
465
+ mm_i_expand(&st_mm);
466
+ }
467
+ }
468
+
469
+ static void
470
+ mm_realloc(i_mm, len)
471
+ mm_ipc *i_mm;
472
+ size_t len;
473
+ {
474
+ if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap");
475
+ if (len > i_mm->t->len) {
476
+ if ((len - i_mm->t->len) < i_mm->t->incr) {
477
+ len = i_mm->t->len + i_mm->t->incr;
478
+ }
479
+ mm_expandf(i_mm, len);
480
+ }
481
+ }
482
+
483
+ /*
484
+ * call-seq:
485
+ * extend(count)
486
+ *
487
+ * add <em>count</em> bytes to the file (i.e. pre-extend the file)
488
+ */
489
+ static VALUE
490
+ mm_extend(obj, a)
491
+ VALUE obj, a;
492
+ {
493
+ mm_ipc *i_mm;
494
+ long len;
495
+
496
+ GetMmap(obj, i_mm, MM_MODIFY);
497
+ len = NUM2LONG(a);
498
+ if (len > 0) {
499
+ mm_expandf(i_mm, i_mm->t->len + len);
500
+ }
501
+ return UINT2NUM(i_mm->t->len);
502
+ }
503
+
504
+ static VALUE
505
+ mm_i_options(arg, obj)
506
+ VALUE arg, obj;
507
+ {
508
+ mm_ipc *i_mm;
509
+ char *options;
510
+ VALUE key, value;
511
+
512
+ Data_Get_Struct(obj, mm_ipc, i_mm);
513
+ key = rb_ary_entry(arg, 0);
514
+ value = rb_ary_entry(arg, 1);
515
+ key = rb_obj_as_string(key);
516
+ options = StringValuePtr(key);
517
+ if (strcmp(options, "length") == 0) {
518
+ i_mm->t->len = NUM2UINT(value);
519
+ if (i_mm->t->len <= 0) {
520
+ rb_raise(rb_eArgError, "Invalid value for length %d", i_mm->t->len);
521
+ }
522
+ i_mm->t->flag |= MM_FIXED;
523
+ }
524
+ else if (strcmp(options, "offset") == 0) {
525
+ i_mm->t->offset = NUM2INT(value);
526
+ if (i_mm->t->offset < 0) {
527
+ rb_raise(rb_eArgError, "Invalid value for offset %d", i_mm->t->offset);
528
+ }
529
+ i_mm->t->flag |= MM_FIXED;
530
+ }
531
+ else if (strcmp(options, "advice") == 0) {
532
+ i_mm->t->advice = NUM2INT(value);
533
+ }
534
+ else if (strcmp(options, "increment") == 0) {
535
+ int incr = NUM2INT(value);
536
+ if (incr < 0) {
537
+ rb_raise(rb_eArgError, "Invalid value for increment %d", incr);
538
+ }
539
+ i_mm->t->incr = incr;
540
+ }
541
+ else if (strcmp(options, "initialize") == 0) {
542
+ }
543
+ #if HAVE_SEMCTL && HAVE_SHMCTL
544
+ else if (strcmp(options, "ipc") == 0) {
545
+ if (value != Qtrue && TYPE(value) != T_HASH) {
546
+ rb_raise(rb_eArgError, "Expected an Hash for :ipc");
547
+ }
548
+ i_mm->t->shmid = value;
549
+ i_mm->t->flag |= (MM_IPC | MM_TMP);
550
+ }
551
+ #endif
552
+ else {
553
+ rb_warning("Unknown option `%s'", options);
554
+ }
555
+ return Qnil;
556
+ }
557
+
558
+ #if HAVE_SEMCTL && HAVE_SHMCTL
559
+
560
+ static VALUE
561
+ mm_i_ipc(arg, obj)
562
+ VALUE arg, obj;
563
+ {
564
+ mm_ipc *i_mm;
565
+ char *options;
566
+ VALUE key, value;
567
+
568
+ Data_Get_Struct(obj, mm_ipc, i_mm);
569
+ key = rb_ary_entry(arg, 0);
570
+ value = rb_ary_entry(arg, 1);
571
+ key = rb_obj_as_string(key);
572
+ options = StringValuePtr(key);
573
+ if (strcmp(options, "key") == 0) {
574
+ i_mm->t->key = rb_funcall2(value, rb_intern("to_int"), 0, 0);
575
+ }
576
+ else if (strcmp(options, "permanent") == 0) {
577
+ if (RTEST(value)) {
578
+ i_mm->t->flag &= ~MM_TMP;
579
+ }
580
+ }
581
+ else if (strcmp(options, "mode") == 0) {
582
+ i_mm->t->semid = NUM2INT(value);
583
+ }
584
+ else {
585
+ rb_warning("Unknown option `%s'", options);
586
+ }
587
+ return Qnil;
588
+ }
589
+
590
+ #endif
591
+
592
+ /*
593
+ * call-seq:
594
+ * new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {})
595
+ *
596
+ * create a new Mmap object
597
+ *
598
+ * * <em>file</em>
599
+ *
600
+ * Pathname of the file, if <em>nil</em> is given an anonymous map
601
+ * is created <em>Mmanp::MAP_ANON</em>
602
+ *
603
+ * * <em>mode</em>
604
+ *
605
+ * Mode to open the file, it can be "r", "w", "rw", "a"
606
+ *
607
+ * * <em>protection</em>
608
+ *
609
+ * specify the nature of the mapping
610
+ *
611
+ * * <em>Mmap::MAP_SHARED</em>
612
+ *
613
+ * Creates a mapping that's shared with all other processes
614
+ * mapping the same areas of the file.
615
+ * The default value is <em>Mmap::MAP_SHARED</em>
616
+ *
617
+ * * <em>Mmap::MAP_PRIVATE</em>
618
+ *
619
+ * Creates a private copy-on-write mapping, so changes to the
620
+ * contents of the mmap object will be private to this process
621
+ *
622
+ * * <em>options</em>
623
+ *
624
+ * Hash. If one of the options <em>length</em> or <em>offset</em>
625
+ * is specified it will not possible to modify the size of
626
+ * the mapped file.
627
+ *
628
+ * length:: maps <em>length</em> bytes from the file
629
+ *
630
+ * offset:: the mapping begin at <em>offset</em>
631
+ *
632
+ * advice:: the type of the access (see #madvise)
633
+ */
634
+ static VALUE
635
+ mm_s_new(argc, argv, obj)
636
+ int argc;
637
+ VALUE *argv, obj;
638
+ {
639
+ VALUE res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
640
+ rb_obj_call_init(res, argc, argv);
641
+ return res;
642
+ }
643
+
644
+ static VALUE
645
+ mm_s_alloc(obj)
646
+ VALUE obj;
647
+ {
648
+ VALUE res;
649
+ mm_ipc *i_mm;
650
+
651
+ res = Data_Make_Struct(obj, mm_ipc, 0, mm_free, i_mm);
652
+ i_mm->t = ALLOC_N(mm_mmap, 1);
653
+ MEMZERO(i_mm->t, mm_mmap, 1);
654
+ i_mm->t->incr = EXP_INCR_SIZE;
655
+ return res;
656
+ }
657
+
658
+ /*
659
+ * call-seq: initialize
660
+ *
661
+ * Create a new Mmap object
662
+ */
663
+ static VALUE
664
+ mm_init(argc, argv, obj)
665
+ VALUE obj, *argv;
666
+ int argc;
667
+ {
668
+ struct stat st;
669
+ int fd, smode = 0, pmode = 0, vscope, perm, init;
670
+ MMAP_RETTYPE addr;
671
+ VALUE fname, fdv, vmode, scope, options;
672
+ mm_ipc *i_mm;
673
+ char *path, *mode;
674
+ size_t size = 0;
675
+ off_t offset;
676
+ int anonymous;
677
+
678
+ options = Qnil;
679
+ if (argc > 1 && TYPE(argv[argc - 1]) == T_HASH) {
680
+ options = argv[argc - 1];
681
+ argc--;
682
+ }
683
+ rb_scan_args(argc, argv, "12", &fname, &vmode, &scope);
684
+ vscope = 0;
685
+ path = 0;
686
+ fd = -1;
687
+ anonymous = 0;
688
+ fdv = Qnil;
689
+ #ifdef MAP_ANON
690
+ if (NIL_P(fname)) {
691
+ vscope = MAP_ANON | MAP_SHARED;
692
+ anonymous = 1;
693
+ }
694
+ else
695
+ #endif
696
+ {
697
+ if (rb_safe_level() > 0 && OBJ_TAINTED(fname)){
698
+ rb_raise(rb_eSecurityError, "Insecure operation");
699
+ }
700
+ rb_secure(4);
701
+ if (rb_respond_to(fname, rb_intern("fileno"))) {
702
+ fdv = rb_funcall2(fname, rb_intern("fileno"), 0, 0);
703
+ }
704
+ if (NIL_P(fdv)) {
705
+ fname = rb_str_to_str(fname);
706
+ SafeStringValue(fname);
707
+ path = StringValuePtr(fname);
708
+ }
709
+ else {
710
+ fd = NUM2INT(fdv);
711
+ if (fd < 0) {
712
+ rb_raise(rb_eArgError, "invalid file descriptor %d", fd);
713
+ }
714
+ }
715
+ if (!NIL_P(scope)) {
716
+ vscope = NUM2INT(scope);
717
+ #ifdef MAP_ANON
718
+ if (vscope & MAP_ANON) {
719
+ rb_raise(rb_eArgError, "filename specified for an anonymous map");
720
+ }
721
+ #endif
722
+ }
723
+ }
724
+ vscope |= NIL_P(scope) ? MAP_SHARED : NUM2INT(scope);
725
+ size = 0;
726
+ perm = 0666;
727
+ if (!anonymous) {
728
+ if (NIL_P(vmode)) {
729
+ mode = "r";
730
+ }
731
+ else if (rb_respond_to(vmode, rb_intern("to_ary"))) {
732
+ VALUE tmp;
733
+
734
+ vmode = rb_convert_type(vmode, T_ARRAY, "Array", "to_ary");
735
+ if (RARRAY(vmode)->len != 2) {
736
+ rb_raise(rb_eArgError, "Invalid length %d (expected 2)",
737
+ RARRAY(vmode)->len);
738
+ }
739
+ tmp = RARRAY(vmode)->ptr[0];
740
+ mode = StringValuePtr(tmp);
741
+ perm = NUM2INT(RARRAY(vmode)->ptr[1]);
742
+ }
743
+ else {
744
+ mode = StringValuePtr(vmode);
745
+ }
746
+ if (strcmp(mode, "r") == 0) {
747
+ smode = O_RDONLY;
748
+ pmode = PROT_READ;
749
+ }
750
+ else if (strcmp(mode, "w") == 0) {
751
+ smode = O_RDWR | O_TRUNC;
752
+ pmode = PROT_READ | PROT_WRITE;
753
+ }
754
+ else if (strcmp(mode, "rw") == 0 || strcmp(mode, "wr") == 0) {
755
+ smode = O_RDWR;
756
+ pmode = PROT_READ | PROT_WRITE;
757
+ }
758
+ else if (strcmp(mode, "a") == 0) {
759
+ smode = O_RDWR | O_CREAT;
760
+ pmode = PROT_READ | PROT_WRITE;
761
+ }
762
+ else {
763
+ rb_raise(rb_eArgError, "Invalid mode %s", mode);
764
+ }
765
+ if (NIL_P(fdv)) {
766
+ if ((fd = open(path, smode, perm)) == -1) {
767
+ rb_raise(rb_eArgError, "Can't open %s", path);
768
+ }
769
+ }
770
+ if (fstat(fd, &st) == -1) {
771
+ rb_raise(rb_eArgError, "Can't stat %s", path);
772
+ }
773
+ size = st.st_size;
774
+ }
775
+ else {
776
+ fd = -1;
777
+ if (!NIL_P(vmode) && TYPE(vmode) != T_STRING) {
778
+ size = NUM2INT(vmode);
779
+ }
780
+ }
781
+ Data_Get_Struct(obj, mm_ipc, i_mm);
782
+ if (i_mm->t->flag & MM_FROZEN) {
783
+ rb_raise(rb_eArgError, "frozen mmap");
784
+ }
785
+ i_mm->t->shmid = 0;
786
+ i_mm->t->semid = 0;
787
+ offset = 0;
788
+ if (options != Qnil) {
789
+ rb_iterate(rb_each, options, mm_i_options, obj);
790
+ if (path && (i_mm->t->len + i_mm->t->offset) > st.st_size) {
791
+ rb_raise(rb_eArgError, "invalid value for length (%d) or offset (%d)",
792
+ i_mm->t->len, i_mm->t->offset);
793
+ }
794
+ if (i_mm->t->len) size = i_mm->t->len;
795
+ offset = i_mm->t->offset;
796
+ #if HAVE_SEMCTL && HAVE_SHMCTL
797
+ if (i_mm->t->flag & MM_IPC) {
798
+ key_t key;
799
+ int shmid, semid, mode;
800
+ union semun sem_val;
801
+ struct shmid_ds buf;
802
+ mm_mmap *data;
803
+
804
+ if (!(vscope & MAP_SHARED)) {
805
+ rb_warning("Probably it will not do what you expect ...");
806
+ }
807
+ i_mm->t->key = -1;
808
+ i_mm->t->semid = 0;
809
+ if (TYPE(i_mm->t->shmid) == T_HASH) {
810
+ rb_iterate(rb_each, i_mm->t->shmid, mm_i_ipc, obj);
811
+ }
812
+ i_mm->t->shmid = 0;
813
+ if (i_mm->t->semid) {
814
+ mode = i_mm->t->semid;
815
+ i_mm->t->semid = 0;
816
+ }
817
+ else {
818
+ mode = 0644;
819
+ }
820
+ if ((int)i_mm->t->key <= 0) {
821
+ mode |= IPC_CREAT;
822
+ strcpy(template, "/tmp/ruby_mmap.XXXXXX");
823
+ if (mkstemp(template) == -1) {
824
+ rb_sys_fail("mkstemp()");
825
+ }
826
+ if ((key = ftok(template, 'R')) == -1) {
827
+ rb_sys_fail("ftok()");
828
+ }
829
+ }
830
+ else {
831
+ key = (key_t)i_mm->t->key;
832
+ }
833
+ if ((shmid = shmget(key, sizeof(mm_ipc), mode)) == -1) {
834
+ rb_sys_fail("shmget()");
835
+ }
836
+ data = shmat(shmid, (void *)0, 0);
837
+ if (data == (mm_mmap *)-1) {
838
+ rb_sys_fail("shmat()");
839
+ }
840
+ if (i_mm->t->flag & MM_TMP) {
841
+ if (shmctl(shmid, IPC_RMID, &buf) == -1) {
842
+ rb_sys_fail("shmctl()");
843
+ }
844
+ }
845
+ if ((semid = semget(key, 1, mode)) == -1) {
846
+ rb_sys_fail("semget()");
847
+ }
848
+ if (mode & IPC_CREAT) {
849
+ sem_val.val = 1;
850
+ if (semctl(semid, 0, SETVAL, sem_val) == -1) {
851
+ rb_sys_fail("semctl()");
852
+ }
853
+ }
854
+ memcpy(data, i_mm->t, sizeof(mm_mmap));
855
+ free(i_mm->t);
856
+ i_mm->t = data;
857
+ i_mm->t->key = key;
858
+ i_mm->t->semid = semid;
859
+ i_mm->t->shmid = shmid;
860
+ if (i_mm->t->flag & MM_TMP) {
861
+ i_mm->t->template = ALLOC_N(char, strlen(template) + 1);
862
+ strcpy(i_mm->t->template, template);
863
+ }
864
+ }
865
+ #endif
866
+ }
867
+ init = 0;
868
+ if (anonymous) {
869
+ if (size <= 0) {
870
+ rb_raise(rb_eArgError, "length not specified for an anonymous map");
871
+ }
872
+ if (offset) {
873
+ rb_warning("Ignoring offset for an anonymous map");
874
+ offset = 0;
875
+ }
876
+ smode = O_RDWR;
877
+ pmode = PROT_READ | PROT_WRITE;
878
+ i_mm->t->flag |= MM_FIXED | MM_ANON;
879
+ }
880
+ else {
881
+ if (size == 0 && (smode & O_RDWR)) {
882
+ if (lseek(fd, i_mm->t->incr - 1, SEEK_END) == -1) {
883
+ rb_raise(rb_eIOError, "Can't lseek %d", i_mm->t->incr - 1);
884
+ }
885
+ if (write(fd, "\000", 1) != 1) {
886
+ rb_raise(rb_eIOError, "Can't extend %s", path);
887
+ }
888
+ init = 1;
889
+ size = i_mm->t->incr;
890
+ }
891
+ if (!NIL_P(fdv)) {
892
+ i_mm->t->flag |= MM_FIXED;
893
+ }
894
+ }
895
+ addr = mmap(0, size, pmode, vscope, fd, offset);
896
+ if (NIL_P(fdv) && !anonymous) {
897
+ close(fd);
898
+ }
899
+ if (addr == MAP_FAILED || !addr) {
900
+ rb_raise(rb_eArgError, "mmap failed (%d)", errno);
901
+ }
902
+ #ifdef MADV_NORMAL
903
+ if (i_mm->t->advice && madvise(addr, size, i_mm->t->advice) == -1) {
904
+ rb_raise(rb_eArgError, "madvise(%d)", errno);
905
+ }
906
+ #endif
907
+ if (anonymous && TYPE(options) == T_HASH) {
908
+ VALUE val;
909
+ char *ptr;
910
+
911
+ val = rb_hash_aref(options, rb_str_new2("initialize"));
912
+ if (!NIL_P(val)) {
913
+ ptr = StringValuePtr(val);
914
+ memset(addr, ptr[0], size);
915
+ }
916
+ }
917
+ i_mm->t->addr = addr;
918
+ i_mm->t->len = size;
919
+ if (!init) i_mm->t->real = size;
920
+ i_mm->t->pmode = pmode;
921
+ i_mm->t->vscope = vscope;
922
+ i_mm->t->smode = smode & ~O_TRUNC;
923
+ i_mm->t->path = (path)?ruby_strdup(path):(char *)-1;
924
+ if (smode == O_RDONLY) {
925
+ obj = rb_obj_freeze(obj);
926
+ i_mm->t->flag |= MM_FROZEN;
927
+ }
928
+ else {
929
+ if (smode == O_WRONLY) {
930
+ i_mm->t->flag |= MM_FIXED;
931
+ }
932
+ OBJ_TAINT(obj);
933
+ }
934
+ return obj;
935
+ }
936
+
937
+ /*
938
+ * Document-method: msync
939
+ * Document-method: sync
940
+ * Document-method: flush
941
+ *
942
+ * call-seq: msync
943
+ *
944
+ * flush the file
945
+ */
946
+ static VALUE
947
+ mm_msync(argc, argv, obj)
948
+ int argc;
949
+ VALUE *argv, obj;
950
+ {
951
+ mm_ipc *i_mm;
952
+ VALUE oflag;
953
+ int ret;
954
+ int flag = MS_SYNC;
955
+
956
+ if (argc) {
957
+ rb_scan_args(argc, argv, "01", &oflag);
958
+ flag = NUM2INT(oflag);
959
+ }
960
+ GetMmap(obj, i_mm, MM_MODIFY);
961
+ if ((ret = msync(i_mm->t->addr, i_mm->t->len, flag)) != 0) {
962
+ rb_raise(rb_eArgError, "msync(%d)", ret);
963
+ }
964
+ if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE)
965
+ mm_expandf(i_mm, i_mm->t->real);
966
+ return obj;
967
+ }
968
+
969
+ /*
970
+ * Document-method: mprotect
971
+ * Document-method: protect
972
+ *
973
+ * call-seq: mprotect(mode)
974
+ *
975
+ * change the mode, value must be "r", "w" or "rw"
976
+ */
977
+ static VALUE
978
+ mm_mprotect(obj, a)
979
+ VALUE obj, a;
980
+ {
981
+ mm_ipc *i_mm;
982
+ int ret, pmode;
983
+ char *smode;
984
+
985
+ GetMmap(obj, i_mm, 0);
986
+ if (TYPE(a) == T_STRING) {
987
+ smode = StringValuePtr(a);
988
+ if (strcmp(smode, "r") == 0) pmode = PROT_READ;
989
+ else if (strcmp(smode, "w") == 0) pmode = PROT_WRITE;
990
+ else if (strcmp(smode, "rw") == 0 || strcmp(smode, "wr") == 0)
991
+ pmode = PROT_READ | PROT_WRITE;
992
+ else {
993
+ rb_raise(rb_eArgError, "Invalid mode %s", smode);
994
+ }
995
+ }
996
+ else {
997
+ pmode = NUM2INT(a);
998
+ }
999
+ if ((pmode & PROT_WRITE) && (i_mm->t->flag & MM_FROZEN))
1000
+ rb_error_frozen("mmap");
1001
+ if ((ret = mprotect(i_mm->t->addr, i_mm->t->len, pmode | PROT_READ)) != 0) {
1002
+ rb_raise(rb_eArgError, "mprotect(%d)", ret);
1003
+ }
1004
+ i_mm->t->pmode = pmode;
1005
+ if (pmode & PROT_READ) {
1006
+ if (pmode & PROT_WRITE) i_mm->t->smode = O_RDWR;
1007
+ else {
1008
+ i_mm->t->smode = O_RDONLY;
1009
+ obj = rb_obj_freeze(obj);
1010
+ i_mm->t->flag |= MM_FROZEN;
1011
+ }
1012
+ }
1013
+ else if (pmode & PROT_WRITE) {
1014
+ i_mm->t->flag |= MM_FIXED;
1015
+ i_mm->t->smode = O_WRONLY;
1016
+ }
1017
+ return obj;
1018
+ }
1019
+
1020
+ #ifdef MADV_NORMAL
1021
+ /*
1022
+ * Document-method: madvise
1023
+ * Document-method: advise
1024
+ *
1025
+ * call-seq: madvise(advice)
1026
+ *
1027
+ * <em>advice</em> can have the value <em>Mmap::MADV_NORMAL</em>,
1028
+ * <em>Mmap::MADV_RANDOM</em>, <em>Mmap::MADV_SEQUENTIAL</em>,
1029
+ * <em>Mmap::MADV_WILLNEED</em>, <em>Mmap::MADV_DONTNEED</em>
1030
+ *
1031
+ */
1032
+ static VALUE
1033
+ mm_madvise(obj, a)
1034
+ VALUE obj, a;
1035
+ {
1036
+ mm_ipc *i_mm;
1037
+
1038
+ GetMmap(obj, i_mm, 0);
1039
+ if (madvise(i_mm->t->addr, i_mm->t->len, NUM2INT(a)) == -1) {
1040
+ rb_raise(rb_eTypeError, "madvise(%d)", errno);
1041
+ }
1042
+ i_mm->t->advice = NUM2INT(a);
1043
+ return Qnil;
1044
+ }
1045
+ #endif
1046
+
1047
+ #define StringMmap(b, bp, bl) \
1048
+ do { \
1049
+ if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \
1050
+ mm_ipc *b_mm; \
1051
+ GetMmap(b, b_mm, 0); \
1052
+ bp = b_mm->t->addr; \
1053
+ bl = b_mm->t->real; \
1054
+ } \
1055
+ else { \
1056
+ bp = StringValuePtr(b); \
1057
+ bl = RSTRING(b)->len; \
1058
+ } \
1059
+ } while (0);
1060
+
1061
+ static void
1062
+ mm_update(str, beg, len, val)
1063
+ mm_ipc *str;
1064
+ VALUE val;
1065
+ long beg;
1066
+ long len;
1067
+ {
1068
+ char *valp;
1069
+ long vall;
1070
+
1071
+ if (str->t->flag & MM_FROZEN) rb_error_frozen("mmap");
1072
+ if (len < 0) rb_raise(rb_eIndexError, "negative length %d", len);
1073
+ mm_lock(str);
1074
+ if (beg < 0) {
1075
+ beg += str->t->real;
1076
+ }
1077
+ if (beg < 0 || str->t->real < (size_t)beg) {
1078
+ if (beg < 0) {
1079
+ beg -= str->t->real;
1080
+ }
1081
+ mm_unlock(str);
1082
+ rb_raise(rb_eIndexError, "index %d out of string", beg);
1083
+ }
1084
+ if (str->t->real < (size_t)(beg + len)) {
1085
+ len = str->t->real - beg;
1086
+ }
1087
+
1088
+ mm_unlock(str);
1089
+ StringMmap(val, valp, vall);
1090
+ mm_lock(str);
1091
+
1092
+ if ((str->t->flag & MM_FIXED) && vall != len) {
1093
+ mm_unlock(str);
1094
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1095
+ }
1096
+ if (len < vall) {
1097
+ mm_realloc(str, str->t->real + vall - len);
1098
+ }
1099
+
1100
+ if (vall != len) {
1101
+ memmove((char *)str->t->addr + beg + vall,
1102
+ (char *)str->t->addr + beg + len,
1103
+ str->t->real - (beg + len));
1104
+ }
1105
+ if (str->t->real < (size_t)beg && len < 0) {
1106
+ MEMZERO(str->t->addr + str->t->real, char, -len);
1107
+ }
1108
+ if (vall > 0) {
1109
+ memmove((char *)str->t->addr + beg, valp, vall);
1110
+ }
1111
+ str->t->real += vall - len;
1112
+ mm_unlock(str);
1113
+ }
1114
+
1115
+ /*
1116
+ * call-seq: =~(other)
1117
+ *
1118
+ * return an index of the match
1119
+ */
1120
+ static VALUE
1121
+ mm_match(x, y)
1122
+ VALUE x, y;
1123
+ {
1124
+ VALUE reg, res;
1125
+ long start;
1126
+
1127
+ x = mm_str(x, MM_ORIGIN);
1128
+ if (TYPE(y) == T_DATA && RDATA(y)->dfree == (RUBY_DATA_FUNC)mm_free) {
1129
+ y = mm_to_str(y);
1130
+ }
1131
+ switch (TYPE(y)) {
1132
+ case T_REGEXP:
1133
+ res = rb_reg_match(y, x);
1134
+ break;
1135
+
1136
+ case T_STRING:
1137
+ reg = rb_reg_regcomp(y);
1138
+ start = rb_reg_search(reg, x, 0, 0);
1139
+ if (start == -1) res = Qnil;
1140
+ else res = INT2NUM(start);
1141
+ break;
1142
+
1143
+ default:
1144
+ res = rb_funcall(y, rb_intern("=~"), 1, x);
1145
+ break;
1146
+ }
1147
+ return res;
1148
+ }
1149
+
1150
+ static VALUE
1151
+ get_pat(pat)
1152
+ VALUE pat;
1153
+ {
1154
+ switch (TYPE(pat)) {
1155
+ case T_REGEXP:
1156
+ break;
1157
+
1158
+ case T_STRING:
1159
+ pat = rb_reg_regcomp(pat);
1160
+ break;
1161
+
1162
+ default:
1163
+ /* type failed */
1164
+ Check_Type(pat, T_REGEXP);
1165
+ }
1166
+ return pat;
1167
+ }
1168
+
1169
+ static int
1170
+ mm_correct_backref()
1171
+ {
1172
+ VALUE match;
1173
+ int i, start;
1174
+
1175
+ match = rb_backref_get();
1176
+ if (NIL_P(match)) return 0;
1177
+ if (RMATCH(match)->BEG(0) == -1) return 0;
1178
+ start = RMATCH(match)->BEG(0);
1179
+ RMATCH(match)->str = rb_str_new(StringValuePtr(RMATCH(match)->str) + start,
1180
+ RMATCH(match)->END(0) - start);
1181
+ if (OBJ_TAINTED(match)) OBJ_TAINT(RMATCH(match)->str);
1182
+ for (i = 0; i < RMATCH(match)->regs->num_regs && RMATCH(match)->BEG(i) != -1; i++) {
1183
+ RMATCH(match)->BEG(i) -= start;
1184
+ RMATCH(match)->END(i) -= start;
1185
+ }
1186
+ rb_backref_set(match);
1187
+ return start;
1188
+ }
1189
+
1190
+ static VALUE
1191
+ mm_sub_bang_int(bang_st)
1192
+ mm_bang *bang_st;
1193
+ {
1194
+ int argc = bang_st->argc;
1195
+ VALUE *argv = bang_st->argv;
1196
+ VALUE obj = bang_st->obj;
1197
+ VALUE pat, repl = Qnil, match, str, res;
1198
+ struct re_registers *regs;
1199
+ int start, iter = 0;
1200
+ int tainted = 0;
1201
+ long plen;
1202
+ mm_ipc *i_mm;
1203
+
1204
+ if (argc == 1 && rb_block_given_p()) {
1205
+ iter = 1;
1206
+ }
1207
+ else if (argc == 2) {
1208
+ repl = rb_str_to_str(argv[1]);
1209
+ if (OBJ_TAINTED(repl)) tainted = 1;
1210
+ }
1211
+ else {
1212
+ rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc);
1213
+ }
1214
+ GetMmap(obj, i_mm, MM_MODIFY);
1215
+ str = mm_str(obj, MM_MODIFY | MM_ORIGIN);
1216
+
1217
+ pat = get_pat(argv[0]);
1218
+ res = Qnil;
1219
+ if (rb_reg_search(pat, str, 0, 0) >= 0) {
1220
+ start = mm_correct_backref();
1221
+ match = rb_backref_get();
1222
+ regs = RMATCH(match)->regs;
1223
+ if (iter) {
1224
+ rb_match_busy(match);
1225
+ repl = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match)));
1226
+ rb_backref_set(match);
1227
+ }
1228
+ else {
1229
+ RSTRING(str)->ptr += start;
1230
+ repl = rb_reg_regsub(repl, str, regs);
1231
+ RSTRING(str)->ptr -= start;
1232
+ }
1233
+ if (OBJ_TAINTED(repl)) tainted = 1;
1234
+ plen = END(0) - BEG(0);
1235
+ if (RSTRING(repl)->len > plen) {
1236
+ mm_realloc(i_mm, RSTRING(str)->len + RSTRING(repl)->len - plen);
1237
+ RSTRING(str)->ptr = i_mm->t->addr;
1238
+ }
1239
+ if (RSTRING(repl)->len != plen) {
1240
+ if (i_mm->t->flag & MM_FIXED) {
1241
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1242
+ }
1243
+ memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(repl)->len,
1244
+ RSTRING(str)->ptr + start + BEG(0) + plen,
1245
+ RSTRING(str)->len - start - BEG(0) - plen);
1246
+ }
1247
+ memcpy(RSTRING(str)->ptr + start + BEG(0),
1248
+ RSTRING(repl)->ptr, RSTRING(repl)->len);
1249
+ i_mm->t->real += RSTRING(repl)->len - plen;
1250
+ if (tainted) OBJ_TAINT(obj);
1251
+
1252
+ res = obj;
1253
+ }
1254
+ rb_gc_force_recycle(str);
1255
+ return res;
1256
+ }
1257
+
1258
+ /*
1259
+ * call-seq:
1260
+ * str.sub!(pattern, replacement) => str or nil
1261
+ * str.sub!(pattern) {|match| block } => str or nil
1262
+ *
1263
+ * substitution
1264
+ */
1265
+ static VALUE
1266
+ mm_sub_bang(argc, argv, obj)
1267
+ int argc;
1268
+ VALUE *argv;
1269
+ VALUE obj;
1270
+ {
1271
+ VALUE res;
1272
+ mm_bang bang_st;
1273
+ mm_ipc *i_mm;
1274
+
1275
+ bang_st.argc = argc;
1276
+ bang_st.argv = argv;
1277
+ bang_st.obj = obj;
1278
+ GetMmap(obj, i_mm, MM_MODIFY);
1279
+ if (i_mm->t->flag & MM_IPC) {
1280
+ mm_lock(i_mm, Qtrue);
1281
+ res = rb_ensure(mm_sub_bang_int, (VALUE)&bang_st, mm_vunlock, obj);
1282
+ }
1283
+ else {
1284
+ res = mm_sub_bang_int(&bang_st);
1285
+ }
1286
+ return res;
1287
+ }
1288
+
1289
+ static VALUE
1290
+ mm_gsub_bang_int(bang_st)
1291
+ mm_bang *bang_st;
1292
+ {
1293
+ int argc = bang_st->argc;
1294
+ VALUE *argv = bang_st->argv;
1295
+ VALUE obj = bang_st->obj;
1296
+ VALUE pat, val, repl = Qnil, match, str;
1297
+ struct re_registers *regs;
1298
+ long beg, offset;
1299
+ int start, iter = 0;
1300
+ int tainted = 0;
1301
+ long plen;
1302
+ mm_ipc *i_mm;
1303
+
1304
+ if (argc == 1 && rb_block_given_p()) {
1305
+ iter = 1;
1306
+ }
1307
+ else if (argc == 2) {
1308
+ repl = rb_str_to_str(argv[1]);
1309
+ if (OBJ_TAINTED(repl)) tainted = 1;
1310
+ }
1311
+ else {
1312
+ rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc);
1313
+ }
1314
+ GetMmap(obj, i_mm, MM_MODIFY);
1315
+ str = mm_str(obj, MM_MODIFY | MM_ORIGIN);
1316
+
1317
+ pat = get_pat(argv[0]);
1318
+ offset = 0;
1319
+ beg = rb_reg_search(pat, str, 0, 0);
1320
+ if (beg < 0) {
1321
+ rb_gc_force_recycle(str);
1322
+ return Qnil;
1323
+ }
1324
+ while (beg >= 0) {
1325
+ start = mm_correct_backref();
1326
+ match = rb_backref_get();
1327
+ regs = RMATCH(match)->regs;
1328
+ if (iter) {
1329
+ rb_match_busy(match);
1330
+ val = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match)));
1331
+ rb_backref_set(match);
1332
+ }
1333
+ else {
1334
+ RSTRING(str)->ptr += start;
1335
+ val = rb_reg_regsub(repl, str, regs);
1336
+ RSTRING(str)->ptr -= start;
1337
+ }
1338
+ if (OBJ_TAINTED(repl)) tainted = 1;
1339
+ plen = END(0) - BEG(0);
1340
+ if ((i_mm->t->real + RSTRING(val)->len - plen) > i_mm->t->len) {
1341
+ mm_realloc(i_mm, RSTRING(str)->len + RSTRING(val)->len - plen);
1342
+ }
1343
+ if (RSTRING(val)->len != plen) {
1344
+ if (i_mm->t->flag & MM_FIXED) {
1345
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1346
+ }
1347
+ memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(val)->len,
1348
+ RSTRING(str)->ptr + start + BEG(0) + plen,
1349
+ RSTRING(str)->len - start - BEG(0) - plen);
1350
+ }
1351
+ memcpy(RSTRING(str)->ptr + start + BEG(0),
1352
+ RSTRING(val)->ptr, RSTRING(val)->len);
1353
+ RSTRING(str)->len += RSTRING(val)->len - plen;
1354
+ i_mm->t->real = RSTRING(str)->len;
1355
+ if (BEG(0) == END(0)) {
1356
+ offset = start + END(0) + mbclen2(RSTRING(str)->ptr[END(0)], pat);
1357
+ offset += RSTRING(val)->len - plen;
1358
+ }
1359
+ else {
1360
+ offset = start + END(0) + RSTRING(val)->len - plen;
1361
+ }
1362
+ if (offset > RSTRING(str)->len) break;
1363
+ beg = rb_reg_search(pat, str, offset, 0);
1364
+ }
1365
+ rb_backref_set(match);
1366
+ if (tainted) OBJ_TAINT(obj);
1367
+ rb_gc_force_recycle(str);
1368
+ return obj;
1369
+ }
1370
+
1371
+ /*
1372
+ * call-seq:
1373
+ * str.gsub!(pattern, replacement) => str or nil
1374
+ * str.gsub!(pattern) {|match| block } => str or nil
1375
+ *
1376
+ * global substitution
1377
+ */
1378
+ static VALUE
1379
+ mm_gsub_bang(argc, argv, obj)
1380
+ int argc;
1381
+ VALUE *argv;
1382
+ VALUE obj;
1383
+ {
1384
+ VALUE res;
1385
+ mm_bang bang_st;
1386
+ mm_ipc *i_mm;
1387
+
1388
+ bang_st.argc = argc;
1389
+ bang_st.argv = argv;
1390
+ bang_st.obj = obj;
1391
+ GetMmap(obj, i_mm, MM_MODIFY);
1392
+ if (i_mm->t->flag & MM_IPC) {
1393
+ mm_lock(i_mm, Qtrue);
1394
+ res = rb_ensure(mm_gsub_bang_int, (VALUE)&bang_st, mm_vunlock, obj);
1395
+ }
1396
+ else {
1397
+ res = mm_gsub_bang_int(&bang_st);
1398
+ }
1399
+ return res;
1400
+ }
1401
+
1402
+ static VALUE mm_index __((int, VALUE *, VALUE));
1403
+
1404
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
1405
+
1406
+ static void
1407
+ mm_subpat_set(obj, re, offset, val)
1408
+ VALUE obj, re;
1409
+ int offset;
1410
+ VALUE val;
1411
+ {
1412
+ VALUE str, match;
1413
+ int start, end, len;
1414
+ mm_ipc *i_mm;
1415
+
1416
+ str = mm_str(obj, MM_MODIFY | MM_ORIGIN);
1417
+ if (rb_reg_search(re, str, 0, 0) < 0) {
1418
+ rb_raise(rb_eIndexError, "regexp not matched");
1419
+ }
1420
+ match = rb_backref_get();
1421
+ if (offset >= RMATCH(match)->regs->num_regs) {
1422
+ rb_raise(rb_eIndexError, "index %d out of regexp", offset);
1423
+ }
1424
+
1425
+ start = RMATCH(match)->BEG(offset);
1426
+ if (start == -1) {
1427
+ rb_raise(rb_eIndexError, "regexp group %d not matched", offset);
1428
+ }
1429
+ end = RMATCH(match)->END(offset);
1430
+ len = end - start;
1431
+ GetMmap(obj, i_mm, MM_MODIFY);
1432
+ mm_update(i_mm, start, len, val);
1433
+ }
1434
+
1435
+ #endif
1436
+
1437
+ static VALUE
1438
+ mm_aset(str, indx, val)
1439
+ VALUE str;
1440
+ VALUE indx, val;
1441
+ {
1442
+ long idx;
1443
+ mm_ipc *i_mm;
1444
+
1445
+ GetMmap(str, i_mm, MM_MODIFY);
1446
+ switch (TYPE(indx)) {
1447
+ case T_FIXNUM:
1448
+ num_index:
1449
+ idx = NUM2INT(indx);
1450
+ if (idx < 0) {
1451
+ idx += i_mm->t->real;
1452
+ }
1453
+ if (idx < 0 || i_mm->t->real <= (size_t)idx) {
1454
+ rb_raise(rb_eIndexError, "index %d out of string", idx);
1455
+ }
1456
+ if (FIXNUM_P(val)) {
1457
+ if (i_mm->t->real == (size_t)idx) {
1458
+ i_mm->t->real += 1;
1459
+ mm_realloc(i_mm, i_mm->t->real);
1460
+ }
1461
+ ((char *)i_mm->t->addr)[idx] = NUM2INT(val) & 0xff;
1462
+ }
1463
+ else {
1464
+ mm_update(i_mm, idx, 1, val);
1465
+ }
1466
+ return val;
1467
+
1468
+ case T_REGEXP:
1469
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
1470
+ mm_subpat_set(str, indx, 0, val);
1471
+ #else
1472
+ {
1473
+ VALUE args[2];
1474
+ args[0] = indx;
1475
+ args[1] = val;
1476
+ mm_sub_bang(2, args, str);
1477
+ }
1478
+ #endif
1479
+ return val;
1480
+
1481
+ case T_STRING:
1482
+ {
1483
+ VALUE res;
1484
+
1485
+ res = mm_index(1, &indx, str);
1486
+ if (!NIL_P(res)) {
1487
+ mm_update(i_mm, NUM2LONG(res), RSTRING(indx)->len, val);
1488
+ }
1489
+ return val;
1490
+ }
1491
+
1492
+ default:
1493
+ /* check if indx is Range */
1494
+ {
1495
+ long beg, len;
1496
+ if (rb_range_beg_len(indx, &beg, &len, i_mm->t->real, 2)) {
1497
+ mm_update(i_mm, beg, len, val);
1498
+ return val;
1499
+ }
1500
+ }
1501
+ idx = NUM2LONG(indx);
1502
+ goto num_index;
1503
+ }
1504
+ }
1505
+
1506
+ /*
1507
+ * call-seq: []=(args)
1508
+ *
1509
+ * Element assignement - with the following syntax
1510
+ *
1511
+ * self[nth] = val
1512
+ *
1513
+ * change the <em>nth</em> character with <em>val</em>
1514
+ *
1515
+ * self[start..last] = val
1516
+ *
1517
+ * change substring from <em>start</em> to <em>last</em> with <em>val</em>
1518
+ *
1519
+ * self[start, len] = val
1520
+ *
1521
+ * replace <em>length</em> characters from <em>start</em> with <em>val</em>.
1522
+ *
1523
+ */
1524
+ static VALUE
1525
+ mm_aset_m(argc, argv, str)
1526
+ int argc;
1527
+ VALUE *argv;
1528
+ VALUE str;
1529
+ {
1530
+ mm_ipc *i_mm;
1531
+
1532
+ GetMmap(str, i_mm, MM_MODIFY);
1533
+ if (argc == 3) {
1534
+ long beg, len;
1535
+
1536
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
1537
+ if (TYPE(argv[0]) == T_REGEXP) {
1538
+ mm_subpat_set(str, argv[0], NUM2INT(argv[1]), argv[2]);
1539
+ }
1540
+ else
1541
+ #endif
1542
+ {
1543
+ beg = NUM2INT(argv[0]);
1544
+ len = NUM2INT(argv[1]);
1545
+ mm_update(i_mm, beg, len, argv[2]);
1546
+ }
1547
+ return argv[2];
1548
+ }
1549
+ if (argc != 2) {
1550
+ rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc);
1551
+ }
1552
+ return mm_aset(str, argv[0], argv[1]);
1553
+ }
1554
+
1555
+ #if HAVE_RB_STR_INSERT
1556
+
1557
+ /*
1558
+ * call-seq: insert(index, str)
1559
+ *
1560
+ * insert <em>str</em> at <em>index</em>
1561
+ */
1562
+ static VALUE
1563
+ mm_insert(str, idx, str2)
1564
+ VALUE str, idx, str2;
1565
+ {
1566
+ mm_ipc *i_mm;
1567
+ long pos = NUM2LONG(idx);
1568
+
1569
+ GetMmap(str, i_mm, MM_MODIFY);
1570
+ if (pos == -1) {
1571
+ pos = RSTRING(str)->len;
1572
+ }
1573
+ else if (pos < 0) {
1574
+ pos++;
1575
+ }
1576
+ mm_update(i_mm, pos, 0, str2);
1577
+ return str;
1578
+ }
1579
+
1580
+ #endif
1581
+
1582
+ static VALUE mm_aref_m _((int, VALUE *, VALUE));
1583
+
1584
+ /*
1585
+ * call-seq: slice!(str)
1586
+ *
1587
+ * delete the specified portion of the file
1588
+ */
1589
+ static VALUE
1590
+ mm_slice_bang(argc, argv, str)
1591
+ int argc;
1592
+ VALUE *argv;
1593
+ VALUE str;
1594
+ {
1595
+ VALUE result;
1596
+ VALUE buf[3];
1597
+ int i;
1598
+
1599
+ if (argc < 1 || 2 < argc) {
1600
+ rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc);
1601
+ }
1602
+ for (i = 0; i < argc; i++) {
1603
+ buf[i] = argv[i];
1604
+ }
1605
+ buf[i] = rb_str_new(0,0);
1606
+ result = mm_aref_m(argc, buf, str);
1607
+ if (!NIL_P(result)) {
1608
+ mm_aset_m(argc+1, buf, str);
1609
+ }
1610
+ return result;
1611
+ }
1612
+
1613
+ static VALUE
1614
+ mm_cat(str, ptr, len)
1615
+ VALUE str;
1616
+ const char *ptr;
1617
+ long len;
1618
+ {
1619
+ mm_ipc *i_mm;
1620
+ char *sptr;
1621
+
1622
+ GetMmap(str, i_mm, MM_MODIFY);
1623
+ if (len > 0) {
1624
+ int poffset = -1;
1625
+ sptr = (char *)i_mm->t->addr;
1626
+
1627
+ if (sptr <= ptr &&
1628
+ ptr < sptr + i_mm->t->real) {
1629
+ poffset = ptr - sptr;
1630
+ }
1631
+ mm_lock(i_mm, Qtrue);
1632
+ mm_realloc(i_mm, i_mm->t->real + len);
1633
+ sptr = (char *)i_mm->t->addr;
1634
+ if (ptr) {
1635
+ if (poffset >= 0) ptr = sptr + poffset;
1636
+ memcpy(sptr + i_mm->t->real, ptr, len);
1637
+ }
1638
+ i_mm->t->real += len;
1639
+ mm_unlock(i_mm);
1640
+ }
1641
+ return str;
1642
+ }
1643
+
1644
+ static VALUE
1645
+ mm_append(str1, str2)
1646
+ VALUE str1, str2;
1647
+ {
1648
+ str2 = rb_str_to_str(str2);
1649
+ str1 = mm_cat(str1, StringValuePtr(str2), RSTRING(str2)->len);
1650
+ return str1;
1651
+ }
1652
+
1653
+ /*
1654
+ * Document-method: concat
1655
+ * Document-method: <<
1656
+ *
1657
+ * call-seq: concat(other)
1658
+ *
1659
+ * append the contents of <em>other</em>
1660
+ */
1661
+ static VALUE
1662
+ mm_concat(str1, str2)
1663
+ VALUE str1, str2;
1664
+ {
1665
+ if (FIXNUM_P(str2)) {
1666
+ int i = FIX2INT(str2);
1667
+ if (0 <= i && i <= 0xff) { /* byte */
1668
+ char c = i;
1669
+ return mm_cat(str1, &c, 1);
1670
+ }
1671
+ }
1672
+ str1 = mm_append(str1, str2);
1673
+ return str1;
1674
+ }
1675
+
1676
+ #ifndef HAVE_RB_STR_LSTRIP
1677
+
1678
+ /*
1679
+ * call-seq: strip!
1680
+ *
1681
+ * removes leading and trailing whitespace
1682
+ */
1683
+ static VALUE
1684
+ mm_strip_bang(str)
1685
+ VALUE str;
1686
+ {
1687
+ char *s, *t, *e;
1688
+ mm_ipc *i_mm;
1689
+
1690
+ GetMmap(str, i_mm, MM_MODIFY);
1691
+ mm_lock(i_mm, Qtrue);
1692
+ s = (char *)i_mm->t->addr;
1693
+ e = t = s + i_mm->t->real;
1694
+ while (s < t && ISSPACE(*s)) s++;
1695
+ t--;
1696
+ while (s <= t && ISSPACE(*t)) t--;
1697
+ t++;
1698
+
1699
+ if (i_mm->t->real != (t - s) && (i_mm->t->flag & MM_FIXED)) {
1700
+ mm_unlock(i_mm);
1701
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1702
+ }
1703
+ i_mm->t->real = t-s;
1704
+ if (s > (char *)i_mm->t->addr) {
1705
+ memmove(i_mm->t->addr, s, i_mm->t->real);
1706
+ ((char *)i_mm->t->addr)[i_mm->t->real] = '\0';
1707
+ }
1708
+ else if (t < e) {
1709
+ ((char *)i_mm->t->addr)[i_mm->t->real] = '\0';
1710
+ }
1711
+ else {
1712
+ str = Qnil;
1713
+ }
1714
+ mm_unlock(i_mm);
1715
+ return str;
1716
+ }
1717
+
1718
+ #else
1719
+
1720
+ /*
1721
+ * call-seq: lstrip!
1722
+ *
1723
+ * removes leading whitespace
1724
+ */
1725
+ static VALUE
1726
+ mm_lstrip_bang(str)
1727
+ VALUE str;
1728
+ {
1729
+ char *s, *t, *e;
1730
+ mm_ipc *i_mm;
1731
+
1732
+ GetMmap(str, i_mm, MM_MODIFY);
1733
+ mm_lock(i_mm, Qtrue);
1734
+ s = (char *)i_mm->t->addr;
1735
+ e = t = s + i_mm->t->real;
1736
+ while (s < t && ISSPACE(*s)) s++;
1737
+
1738
+ if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) {
1739
+ mm_unlock(i_mm);
1740
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1741
+ }
1742
+ i_mm->t->real = t - s;
1743
+ if (s > (char *)i_mm->t->addr) {
1744
+ memmove(i_mm->t->addr, s, i_mm->t->real);
1745
+ ((char *)i_mm->t->addr)[i_mm->t->real] = '\0';
1746
+ mm_unlock(i_mm);
1747
+ return str;
1748
+ }
1749
+ mm_unlock(i_mm);
1750
+ return Qnil;
1751
+ }
1752
+
1753
+ /*
1754
+ * call-seq: rstrip!
1755
+ *
1756
+ * removes trailing whitespace
1757
+ */
1758
+ static VALUE
1759
+ mm_rstrip_bang(str)
1760
+ VALUE str;
1761
+ {
1762
+ char *s, *t, *e;
1763
+ mm_ipc *i_mm;
1764
+
1765
+ GetMmap(str, i_mm, MM_MODIFY);
1766
+ mm_lock(i_mm, Qtrue);
1767
+ s = (char *)i_mm->t->addr;
1768
+ e = t = s + i_mm->t->real;
1769
+ t--;
1770
+ while (s <= t && ISSPACE(*t)) t--;
1771
+ t++;
1772
+ if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) {
1773
+ mm_unlock(i_mm);
1774
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
1775
+ }
1776
+ i_mm->t->real = t - s;
1777
+ if (t < e) {
1778
+ ((char *)i_mm->t->addr)[i_mm->t->real] = '\0';
1779
+ mm_unlock(i_mm);
1780
+ return str;
1781
+ }
1782
+ mm_unlock(i_mm);
1783
+ return Qnil;
1784
+ }
1785
+
1786
+ static VALUE
1787
+ mm_strip_bang(str)
1788
+ VALUE str;
1789
+ {
1790
+ VALUE l = mm_lstrip_bang(str);
1791
+ VALUE r = mm_rstrip_bang(str);
1792
+
1793
+ if (NIL_P(l) && NIL_P(r)) return Qnil;
1794
+ return str;
1795
+ }
1796
+
1797
+ #endif
1798
+
1799
+ #define MmapStr(b, recycle) \
1800
+ do { \
1801
+ recycle = 0; \
1802
+ if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \
1803
+ recycle = 1; \
1804
+ b = mm_str(b, MM_ORIGIN); \
1805
+ } \
1806
+ else { \
1807
+ b = rb_str_to_str(b); \
1808
+ } \
1809
+ } while (0);
1810
+
1811
+
1812
+ /*
1813
+ * call-seq: <=>(other)
1814
+ *
1815
+ * comparison : return -1, 0, 1
1816
+ */
1817
+ static VALUE
1818
+ mm_cmp(a, b)
1819
+ VALUE a, b;
1820
+ {
1821
+ int result;
1822
+ int recycle = 0;
1823
+
1824
+ a = mm_str(a, MM_ORIGIN);
1825
+ MmapStr(b, recycle);
1826
+ result = rb_str_cmp(a, b);
1827
+ rb_gc_force_recycle(a);
1828
+ if (recycle) rb_gc_force_recycle(b);
1829
+ return INT2FIX(result);
1830
+ }
1831
+
1832
+ #if HAVE_RB_STR_CASECMP
1833
+
1834
+ /*
1835
+ * call-seq: casecmp(other)
1836
+ *
1837
+ * only with ruby >= 1.7.1
1838
+ */
1839
+ static VALUE
1840
+ mm_casecmp(a, b)
1841
+ VALUE a, b;
1842
+ {
1843
+ VALUE result;
1844
+ int recycle = 0;
1845
+
1846
+ a = mm_str(a, MM_ORIGIN);
1847
+ MmapStr(b, recycle);
1848
+ result = rb_funcall2(a, rb_intern("casecmp"), 1, &b);
1849
+ rb_gc_force_recycle(a);
1850
+ if (recycle) rb_gc_force_recycle(b);
1851
+ return result;
1852
+ }
1853
+
1854
+ #endif
1855
+
1856
+ /*
1857
+ * Document-method: ==
1858
+ * Document-method: ===
1859
+ *
1860
+ * call-seq: ==
1861
+ *
1862
+ * comparison
1863
+ */
1864
+ static VALUE
1865
+ mm_equal(a, b)
1866
+ VALUE a, b;
1867
+ {
1868
+ VALUE result;
1869
+ mm_ipc *i_mm, *u_mm;
1870
+
1871
+ if (a == b) return Qtrue;
1872
+ if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free)
1873
+ return Qfalse;
1874
+
1875
+ GetMmap(a, i_mm, 0);
1876
+ GetMmap(b, u_mm, 0);
1877
+ if (i_mm->t->real != u_mm->t->real)
1878
+ return Qfalse;
1879
+ a = mm_str(a, MM_ORIGIN);
1880
+ b = mm_str(b, MM_ORIGIN);
1881
+ result = rb_funcall2(a, rb_intern("=="), 1, &b);
1882
+ rb_gc_force_recycle(a);
1883
+ rb_gc_force_recycle(b);
1884
+ return result;
1885
+ }
1886
+
1887
+ /*
1888
+ * call-seq: eql?(other)
1889
+ *
1890
+ * Is this eql? to +other+ ?
1891
+ */
1892
+ static VALUE
1893
+ mm_eql(a, b)
1894
+ VALUE a, b;
1895
+ {
1896
+ VALUE result;
1897
+ mm_ipc *i_mm, *u_mm;
1898
+
1899
+ if (a == b) return Qtrue;
1900
+ if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free)
1901
+ return Qfalse;
1902
+
1903
+ GetMmap(a, i_mm, 0);
1904
+ GetMmap(b, u_mm, 0);
1905
+ if (i_mm->t->real != u_mm->t->real)
1906
+ return Qfalse;
1907
+ a = mm_str(a, MM_ORIGIN);
1908
+ b = mm_str(b, MM_ORIGIN);
1909
+ result = rb_funcall2(a, rb_intern("eql?"), 1, &b);
1910
+ rb_gc_force_recycle(a);
1911
+ rb_gc_force_recycle(b);
1912
+ return result;
1913
+ }
1914
+
1915
+ /*
1916
+ * call-seq: hash
1917
+ *
1918
+ * Get the hash value
1919
+ */
1920
+ static VALUE
1921
+ mm_hash(a)
1922
+ VALUE a;
1923
+ {
1924
+ VALUE b;
1925
+ int res;
1926
+
1927
+ b = mm_str(a, MM_ORIGIN);
1928
+ res = rb_str_hash(b);
1929
+ rb_gc_force_recycle(b);
1930
+ return INT2FIX(res);
1931
+ }
1932
+
1933
+ /*
1934
+ * Document-method: length
1935
+ * Document-method: size
1936
+ *
1937
+ * return the size of the file
1938
+ */
1939
+ static VALUE
1940
+ mm_size(a)
1941
+ VALUE a;
1942
+ {
1943
+ mm_ipc *i_mm;
1944
+
1945
+ GetMmap(a, i_mm, 0);
1946
+ return UINT2NUM(i_mm->t->real);
1947
+ }
1948
+
1949
+ /*
1950
+ * call-seq: empty?
1951
+ *
1952
+ * return <em>true</em> if the file is empty
1953
+ */
1954
+ static VALUE
1955
+ mm_empty(a)
1956
+ VALUE a;
1957
+ {
1958
+ mm_ipc *i_mm;
1959
+
1960
+ GetMmap(a, i_mm, 0);
1961
+ if (i_mm->t->real == 0) return Qtrue;
1962
+ return Qfalse;
1963
+ }
1964
+
1965
+ static VALUE
1966
+ mm_protect_bang(t)
1967
+ VALUE *t;
1968
+ {
1969
+ return rb_funcall2(t[0], (ID)t[1], (int)t[2], (VALUE *)t[3]);
1970
+ }
1971
+
1972
+ static VALUE
1973
+ mm_recycle(str)
1974
+ VALUE str;
1975
+ {
1976
+ rb_gc_force_recycle(str);
1977
+ return str;
1978
+ }
1979
+
1980
+ static VALUE
1981
+ mm_i_bang(bang_st)
1982
+ mm_bang *bang_st;
1983
+ {
1984
+ VALUE str, res;
1985
+ mm_ipc *i_mm;
1986
+
1987
+ str = mm_str(bang_st->obj, bang_st->flag);
1988
+ if (bang_st->flag & MM_PROTECT) {
1989
+ VALUE tmp[4];
1990
+ tmp[0] = str;
1991
+ tmp[1] = (VALUE)bang_st->id;
1992
+ tmp[2] = (VALUE)bang_st->argc;
1993
+ tmp[3] = (VALUE)bang_st->argv;
1994
+ res = rb_ensure(mm_protect_bang, (VALUE)tmp, mm_recycle, str);
1995
+ }
1996
+ else {
1997
+ res = rb_funcall2(str, bang_st->id, bang_st->argc, bang_st->argv);
1998
+ rb_gc_force_recycle(str);
1999
+ }
2000
+ if (res != Qnil) {
2001
+ GetMmap(bang_st->obj, i_mm, 0);
2002
+ i_mm->t->real = RSTRING(str)->len;
2003
+ }
2004
+ return res;
2005
+ }
2006
+
2007
+
2008
+ static VALUE
2009
+ mm_bang_i(obj, flag, id, argc, argv)
2010
+ VALUE obj, *argv;
2011
+ int flag, id, argc;
2012
+ {
2013
+ VALUE res;
2014
+ mm_ipc *i_mm;
2015
+ mm_bang bang_st;
2016
+
2017
+ GetMmap(obj, i_mm, 0);
2018
+ if ((flag & MM_CHANGE) && (i_mm->t->flag & MM_FIXED)) {
2019
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
2020
+ }
2021
+ bang_st.obj = obj;
2022
+ bang_st.flag = flag;
2023
+ bang_st.id = id;
2024
+ bang_st.argc = argc;
2025
+ bang_st.argv = argv;
2026
+ if (i_mm->t->flag & MM_IPC) {
2027
+ mm_lock(i_mm, Qtrue);
2028
+ res = rb_ensure(mm_i_bang, (VALUE)&bang_st, mm_vunlock, obj);
2029
+ }
2030
+ else {
2031
+ res = mm_i_bang(&bang_st);
2032
+ }
2033
+ if (res == Qnil) return res;
2034
+ return (flag & MM_ORIGIN)?res:obj;
2035
+
2036
+ }
2037
+
2038
+ #if HAVE_RB_STR_MATCH
2039
+
2040
+ /*
2041
+ * call-seq: match(pattern)
2042
+ *
2043
+ * convert <em>pattern</em> to a <em>Regexp</em> and then call
2044
+ * <em>match</em> on <em>self</em>
2045
+ */
2046
+ static VALUE
2047
+ mm_match_m(a, b)
2048
+ VALUE a, b;
2049
+ {
2050
+ return mm_bang_i(a, MM_ORIGIN, rb_intern("match"), 1, &b);
2051
+ }
2052
+
2053
+ #endif
2054
+
2055
+ /*
2056
+ * call-seq: upcase!
2057
+ *
2058
+ * replaces all lowercase characters to downcase characters
2059
+ */
2060
+ static VALUE
2061
+ mm_upcase_bang(a)
2062
+ VALUE a;
2063
+ {
2064
+ return mm_bang_i(a, MM_MODIFY, rb_intern("upcase!"), 0, 0);
2065
+ }
2066
+
2067
+ /*
2068
+ * call-seq: downcase!
2069
+ *
2070
+ * change all uppercase character to lowercase character
2071
+ */
2072
+ static VALUE
2073
+ mm_downcase_bang(a)
2074
+ VALUE a;
2075
+ {
2076
+ return mm_bang_i(a, MM_MODIFY, rb_intern("downcase!"), 0, 0);
2077
+ }
2078
+
2079
+ /*
2080
+ * call-seq: capitalize!
2081
+ *
2082
+ * change the first character to uppercase letter
2083
+ */
2084
+ static VALUE
2085
+ mm_capitalize_bang(a)
2086
+ VALUE a;
2087
+ {
2088
+ return mm_bang_i(a, MM_MODIFY, rb_intern("capitalize!"), 0, 0);
2089
+ }
2090
+
2091
+ /*
2092
+ * call-seq: swapcase!
2093
+ *
2094
+ * replaces all lowercase characters to uppercase characters, and vice-versa
2095
+ */
2096
+ static VALUE
2097
+ mm_swapcase_bang(a)
2098
+ VALUE a;
2099
+ {
2100
+ return mm_bang_i(a, MM_MODIFY, rb_intern("swapcase!"), 0, 0);
2101
+ }
2102
+
2103
+ /*
2104
+ * call-seq: reverse!
2105
+ *
2106
+ * reverse the content of the file
2107
+ */
2108
+ static VALUE
2109
+ mm_reverse_bang(a)
2110
+ VALUE a;
2111
+ {
2112
+ return mm_bang_i(a, MM_MODIFY, rb_intern("reverse!"), 0, 0);
2113
+ }
2114
+
2115
+ /*
2116
+ * call-seq: chop!
2117
+ *
2118
+ * chop off the last character
2119
+ */
2120
+ static VALUE
2121
+ mm_chop_bang(a)
2122
+ VALUE a;
2123
+ {
2124
+ return mm_bang_i(a, MM_CHANGE, rb_intern("chop!"), 0, 0);
2125
+ }
2126
+
2127
+ /*
2128
+ * call-seq: chomp!(rs = $/)
2129
+ *
2130
+ * chop off the line ending character, specified by <em>rs</em>
2131
+ */
2132
+ static VALUE
2133
+ mm_chomp_bang(argc, argv, obj)
2134
+ int argc;
2135
+ VALUE *argv, obj;
2136
+ {
2137
+ return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("chomp!"), argc, argv);
2138
+ }
2139
+
2140
+ /*
2141
+ * call-seq: delete!(str)
2142
+ *
2143
+ * delete every characters included in <em>str</em>
2144
+ */
2145
+ static VALUE
2146
+ mm_delete_bang(argc, argv, obj)
2147
+ int argc;
2148
+ VALUE *argv, obj;
2149
+ {
2150
+ return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("delete!"), argc, argv);
2151
+ }
2152
+
2153
+ /*
2154
+ * squeeze!(str)
2155
+ *
2156
+ * squeezes sequences of the same characters which is included in <em>str</em>
2157
+ */
2158
+ static VALUE
2159
+ mm_squeeze_bang(argc, argv, obj)
2160
+ int argc;
2161
+ VALUE *argv, obj;
2162
+ {
2163
+ return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("squeeze!"), argc, argv);
2164
+ }
2165
+
2166
+ /*
2167
+ * call-seq: tr!(search, replace)
2168
+ *
2169
+ * translate the character from <em>search</em> to <em>replace</em>
2170
+ */
2171
+ static VALUE
2172
+ mm_tr_bang(obj, a, b)
2173
+ VALUE obj, a, b;
2174
+ {
2175
+ VALUE tmp[2];
2176
+ tmp[0] = a;
2177
+ tmp[1] = b;
2178
+ return mm_bang_i(obj, MM_MODIFY | MM_PROTECT, rb_intern("tr!"), 2, tmp);
2179
+ }
2180
+
2181
+ /*
2182
+ * call-seq: tr_s!(search, replace)
2183
+ *
2184
+ * translate the character from <em>search</em> to <em>replace</em>, then
2185
+ * squeeze sequence of the same characters
2186
+ */
2187
+ static VALUE
2188
+ mm_tr_s_bang(obj, a, b)
2189
+ VALUE obj, a, b;
2190
+ {
2191
+ VALUE tmp[2];
2192
+ tmp[0] = a;
2193
+ tmp[1] = b;
2194
+ return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("tr_s!"), 2, tmp);
2195
+ }
2196
+
2197
+ /*
2198
+ * call-seq: crypt
2199
+ *
2200
+ * crypt with <em>salt</em>
2201
+ */
2202
+ static VALUE
2203
+ mm_crypt(a, b)
2204
+ VALUE a, b;
2205
+ {
2206
+ return mm_bang_i(a, MM_ORIGIN, rb_intern("crypt"), 1, &b);
2207
+ }
2208
+
2209
+ /*
2210
+ * call-seq: include?(other)
2211
+ *
2212
+ * return <em>true</em> if <em>other</em> is found
2213
+ */
2214
+ static VALUE
2215
+ mm_include(a, b)
2216
+ VALUE a, b;
2217
+ {
2218
+ return mm_bang_i(a, MM_ORIGIN, rb_intern("include?"), 1, &b);
2219
+ }
2220
+
2221
+ /*
2222
+ * call-seq: index
2223
+ *
2224
+ * return the index of <em>substr</em>
2225
+ */
2226
+ static VALUE
2227
+ mm_index(argc, argv, obj)
2228
+ int argc;
2229
+ VALUE *argv, obj;
2230
+ {
2231
+ return mm_bang_i(obj, MM_ORIGIN, rb_intern("index"), argc, argv);
2232
+ }
2233
+
2234
+ /*
2235
+ * call-seq: rindex(sibstr, pos = nil)
2236
+ *
2237
+ * return the index of the last occurrence of <em>substr</em>
2238
+ */
2239
+ static VALUE
2240
+ mm_rindex(argc, argv, obj)
2241
+ int argc;
2242
+ VALUE *argv, obj;
2243
+ {
2244
+ return mm_bang_i(obj, MM_ORIGIN, rb_intern("rindex"), argc, argv);
2245
+ }
2246
+
2247
+ /*
2248
+ * Document-method: []
2249
+ * Document-method: slice
2250
+ *
2251
+ * call-seq: [](args)
2252
+ *
2253
+ * Element reference - with the following syntax:
2254
+ *
2255
+ * self[nth]
2256
+ *
2257
+ * retrieve the <em>nth</em> character
2258
+ *
2259
+ * self[start..last]
2260
+ *
2261
+ * return a substring from <em>start</em> to <em>last</em>
2262
+ *
2263
+ * self[start, length]
2264
+ *
2265
+ * return a substring of <em>lenght</em> characters from <em>start</em>
2266
+ */
2267
+ static VALUE
2268
+ mm_aref_m(argc, argv, obj)
2269
+ int argc;
2270
+ VALUE *argv, obj;
2271
+ {
2272
+ return mm_bang_i(obj, MM_ORIGIN, rb_intern("[]"), argc, argv);
2273
+ }
2274
+
2275
+ /*
2276
+ * call-seq: sum(bits = 16)
2277
+ *
2278
+ * return a checksum
2279
+ */
2280
+ static VALUE
2281
+ mm_sum(argc, argv, obj)
2282
+ int argc;
2283
+ VALUE *argv, obj;
2284
+ {
2285
+ return mm_bang_i(obj, MM_ORIGIN, rb_intern("sum"), argc, argv);
2286
+ }
2287
+
2288
+ /*
2289
+ * call-seq: split(sep, limit = 0)
2290
+ *
2291
+ * splits into a list of strings and return this array
2292
+ */
2293
+ static VALUE
2294
+ mm_split(argc, argv, obj)
2295
+ int argc;
2296
+ VALUE *argv, obj;
2297
+ {
2298
+ return mm_bang_i(obj, MM_ORIGIN, rb_intern("split"), argc, argv);
2299
+ }
2300
+
2301
+ /*