mmap2-2.2.7/0000755000175000017500000000000013155520113011556 5ustar pravipravimmap2-2.2.7/Changes0000644000175000017500000000177613155520113013064 0ustar pravipravi -- 0.1.2 * fixed some bugs (options & new) (Thanks Joseph McDonald ) * added anonymous map * added mlock, munlock, etc -- 0.1.3 * added #count, #slice, #slice! * added #insert, #casecmp (>= 171) * corrected NEW2LONG -- 0.1.4 * added #lstrip!, #rstrip! for 1.7.1 * corrected strip! * corrected mm_bang_i (reverse!, etc) * added a small test (make test) -- 0.1.5 * added str[/a(a)/, 1] for 1.7.1 * corrected mm_aset (mm_update(str, ...)) * corrected ==, ===, eql? * corrected mm_sub_bang, mm_gsub_bang (to_str) -- 0.1.6 * adapted for 1.7.2 (mm_str) * corrected real for mm_sub_bang * protected against old class -- 0.1.7 * 1.7.2 (::allocate, #initialize) * added "a" for ::new * experimental EXP_INCR_SIZE (4096) ("increment" => 4096) * tests for RUNIT/Test::Unit -- 0.1.8 * test for madvise(2) * don't test size for MAP_ANON * added syntax Mmap(nil, length) * documentation : make rd2; make rdoc -- 0.1.9 * String#slice! was modified in 1.6.8 * added ::new(IO) --- 0.2.0 * adapted for 1.8.0 mmap2-2.2.7/mmap2.gemspec0000644000175000017500000000237613155520113014147 0ustar pravipravi######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: mmap2 2.2.7 ruby lib # stub: ext/mmap/extconf.rb Gem::Specification.new do |s| s.name = "mmap2".freeze s.version = "2.2.7" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Guy Decoux".freeze, "Aaron Patterson".freeze, "Kevin Lyda".freeze] s.date = "2017-06-27" s.description = "The Mmap class implement memory-mapped file objects for Ruby 2.x".freeze s.email = ["ts@moulon.inra.fr".freeze, "tenderlove@github.com".freeze, "lyda@gitlab.com".freeze] s.extensions = ["ext/mmap/extconf.rb".freeze] s.files = ["Changes".freeze, "README.rdoc".freeze, "b.rb".freeze, "ext/mmap/extconf.rb".freeze, "ext/mmap/mmap.c".freeze, "lib/mmap.rb".freeze, "lib/mmap/version.rb".freeze, "mmap.rd".freeze, "test/test_mmap.rb".freeze] s.homepage = "https://gitlab.com/lyda/mmap".freeze s.licenses = ["Ruby".freeze] s.rubygems_version = "2.5.2".freeze s.summary = "The Mmap class".freeze s.test_files = ["b.rb".freeze, "test/test_mmap.rb".freeze] end mmap2-2.2.7/mmap.rd0000644000175000017500000001312413155520113013040 0ustar pravipravi=begin = Mmap (()) The Mmap class implement memory-mapped file objects === WARNING === The variables $' and $` are not available with gsub! and sub! == SuperClass Object == Included Modules * Comparable * Enumerable == Class Methods --- lockall(flag) disable paging of all pages mapped. ((|flag|)) can be ((|Mmap::MCL_CURRENT|)) or ((|Mmap::MCL_FUTURE|)) --- new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {}) --- new(nil, length, protection = Mmap::MAP_SHARED, options = {}) create a new Mmap object : ((|file|)) Pathname of the file, if ((|nil|)) is given an anonymous map is created ((|Mmanp::MAP_ANON|)) : ((|mode|)) Mode to open the file, it can be "r", "w", "rw", "a" : ((|protection|)) specify the nature of the mapping : ((|Mmap::MAP_SHARED|)) Creates a mapping that's shared with all other processes mapping the same areas of the file. The default value is ((|Mmap::MAP_SHARED|)) : ((|Mmap::MAP_PRIVATE|)) Creates a private copy-on-write mapping, so changes to the contents of the mmap object will be private to this process : ((|options|)) Hash. If one of the options ((|length|)) or ((|offset|)) is specified it will not possible to modify the size of the mapped file. : ((|length|)) Maps ((|length|)) bytes from the file : ((|offset|)) The mapping begin at ((|offset|)) : ((|advice|)) The type of the access (see #madvise) --- unlockall reenable paging == Methods --- extend(count) add ((|count|)) bytes to the file (i.e. pre-extend the file) --- madvise(advice) ((|advice|)) can have the value ((|Mmap::MADV_NORMAL|)), ((|Mmap::MADV_RANDOM|)), ((|Mmap::MADV_SEQUENTIAL|)), ((|Mmap::MADV_WILLNEED|)), ((|Mmap::MADV_DONTNEED|)) --- mprotect(mode) change the mode, value must be "r", "w" or "rw" --- mlock disable paging --- msync --- flush flush the file --- munlock reenable paging --- munmap terminate the association === Other methods with the same syntax than for the class String --- self == other comparison --- self > other comparison --- self >= other comparison --- self < other comparison --- self <= other comparison --- self === other used for ((|case|)) comparison --- self << other append ((|other|)) to ((|self|)) --- self =~ other return an index of the match --- self[nth] retrieve the ((|nth|)) character --- self[start..last] return a substring from ((|start|)) to ((|last|)) --- self[start, length] return a substring of ((|lenght|)) characters from ((|start|)) --- self[nth] = val change the ((|nth|)) character with ((|val|)) --- self[start..last] = val change substring from ((|start|)) to ((|last|)) with ((|val|)) --- self[start, len] = val replace ((|length|)) characters from ((|start|)) with ((|val|)). --- self <=> other comparison : return -1, 0, 1 --- casecmp(other) >= 1.7.1 --- concat(other) append the contents of ((|other|)) --- capitalize! change the first character to uppercase letter --- chop! chop off the last character --- chomp!([rs]) chop off the line ending character, specified by ((|rs|)) --- count(o1 [, o2, ...]) each parameter defines a set of character to count --- crypt(salt) crypt with ((|salt|)) --- delete!(str) delete every characters included in ((|str|)) --- downcase! change all uppercase character to lowercase character --- each_byte {|char|...} iterate on each byte --- each([rs]) {|line|...} --- each_line([rs]) {|line|...} iterate on each line --- empty? return ((|true|)) if the file is empty --- freeze freeze the current file --- frozen return ((|true|)) if the file is frozen --- gsub!(pattern, replace) global substitution --- gsub!(pattern) {|str|...} global substitution --- include?(other) return ((|true|)) if ((|other|)) is found --- index(substr[, pos]) return the index of ((|substr|)) --- insert(index, str) >= 1.7.1 insert ((|str|)) at ((|index|)) --- length return the size of the file --- reverse! reverse the content of the file --- rindex(substr[, pos]) return the index of the last occurrence of ((|substr|)) --- scan(pattern) return an array of all occurence matched by ((|pattern|)) --- scan(pattern) {|str| ...} iterate through the file, matching the ((|pattern|)) --- size return the size of the file --- slice same than ((|[]|)) --- slice! delete the specified portion of the file --- split([sep[, limit]]) splits into a list of strings and return this array --- squeeze!([str]) squeezes sequences of the same characters which is included in ((|str|)) --- strip! removes leading and trailing whitespace --- sub!(pattern, replace) substitution --- sub!(pattern) {|str| ...} substitution --- sum([bits]) return a checksum --- swapcase! replaces all lowercase characters to uppercase characters, and vice-versa --- tr!(search, replace) translate the character from ((|search|)) to ((|replace|)) --- tr_s!(search, replace) translate the character from ((|search|)) to ((|replace|)), then squeeze sequence of the same characters --- upcase! replaces all lowercase characters to downcase characters =end mmap2-2.2.7/lib/0000755000175000017500000000000013155520113012324 5ustar pravipravimmap2-2.2.7/lib/mmap.rb0000644000175000017500000000077613155520113013615 0ustar pravipravi# The Mmap class implement memory-mapped file objects # # Most of these methods have the same syntax than the methods of String # # === WARNING # === The variables $' and $` are not available with gsub! and sub! require 'mmap/mmap' unless defined? Mmap require 'mmap/version' class Mmap include Comparable include Enumerable def clone # :nodoc: raise TypeError, "can't clone instance of #{self.class}" end def dup # :nodoc: raise TypeError, "can't dup instance of #{self.class}" end end mmap2-2.2.7/lib/mmap/0000755000175000017500000000000013155520113013256 5ustar pravipravimmap2-2.2.7/lib/mmap/version.rb0000644000175000017500000000007013155520113015265 0ustar pravipravi# Version of mmap2. class Mmap VERSION = '2.2.7' end mmap2-2.2.7/ext/0000755000175000017500000000000013155520113012356 5ustar pravipravimmap2-2.2.7/ext/mmap/0000755000175000017500000000000013155520113013310 5ustar pravipravimmap2-2.2.7/ext/mmap/extconf.rb0000755000175000017500000000072013155520113015305 0ustar pravipravi#!/usr/bin/ruby ARGV.collect! {|x| x.sub(/^--with-mmap-prefix=/, "--with-mmap-dir=") } require 'mkmf' dir_config("mmap") ["lstrip", "match", "insert", "casecmp"].each do |func| if "aa".respond_to?(func) $CFLAGS += " -DHAVE_RB_STR_#{func.upcase}" end end have_func 'rb_fstring_new' if enable_config("ipc") unless have_func("semctl") && have_func("shmctl") $stderr.puts "\tIPC will not be available" end end create_makefile "mmap/mmap" mmap2-2.2.7/ext/mmap/mmap.c0000644000175000017500000016126613155520113014422 0ustar pravipravi#include #include #include #include #include #include #include #if HAVE_SEMCTL && HAVE_SHMCTL #include #include #include #endif #include #include #ifndef StringValue #define StringValue(x) do { \ if (TYPE(x) != T_STRING) x = rb_str_to_str(x); \ } while (0) #endif #ifndef StringValuePtr #define StringValuePtr(x) STR2CSTR(x) #endif #ifndef SafeStringValue #define SafeStringValue(x) Check_SafeStr(x) #endif #ifndef MADV_NORMAL #ifdef POSIX_MADV_NORMAL #define MADV_NORMAL POSIX_MADV_NORMAL #define MADV_RANDOM POSIX_MADV_RANDOM #define MADV_SEQUENTIAL POSIX_MADV_SEQUENTIAL #define MADV_WILLNEED POSIX_MADV_WILLNEED #define MADV_DONTNEED POSIX_MADV_DONTNEED #define madvise posix_madvise #endif #endif #define BEG(no) rmatch->regs.beg[no] #define END(no) rmatch->regs.end[no] #ifndef MMAP_RETTYPE #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309 #endif /* !_POSIX_C_SOURCE */ #ifdef _POSIX_VERSION #if _POSIX_VERSION >= 199309 #define MMAP_RETTYPE void * #endif /* _POSIX_VERSION >= 199309 */ #endif /* _POSIX_VERSION */ #endif /* !MMAP_RETTYPE */ #ifndef MMAP_RETTYPE #define MMAP_RETTYPE caddr_t #endif #ifndef MAP_FAILED #define MAP_FAILED ((caddr_t)-1) #endif /* !MAP_FAILED */ #ifndef MAP_ANON #ifdef MAP_ANONYMOUS #define MAP_ANON MAP_ANONYMOUS #endif #endif static VALUE mm_cMap; #define EXP_INCR_SIZE 4096 typedef struct { MMAP_RETTYPE addr; int smode, pmode, vscope; int advice, flag; VALUE key; int semid, shmid; size_t len, real, incr; off_t offset; char *path, *template; } mm_mmap; typedef struct { int count; mm_mmap *t; } mm_ipc; typedef struct { VALUE obj, *argv; int flag, id, argc; } mm_bang; #define MM_MODIFY 1 #define MM_ORIGIN 2 #define MM_CHANGE (MM_MODIFY | 4) #define MM_PROTECT 8 #define MM_FROZEN (1<<0) #define MM_FIXED (1<<1) #define MM_ANON (1<<2) #define MM_LOCK (1<<3) #define MM_IPC (1<<4) #define MM_TMP (1<<5) #if HAVE_SEMCTL && HAVE_SHMCTL static char template[1024]; union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; #endif static void mm_free(mm_ipc * i_mm) { #if HAVE_SEMCTL && HAVE_SHMCTL if (i_mm->t->flag & MM_IPC) { struct shmid_ds buf; if (shmctl(i_mm->t->shmid, IPC_STAT, &buf) != -1) { if (buf.shm_nattch == 1 && (i_mm->t->flag & MM_TMP)) { semctl(i_mm->t->semid, 0, IPC_RMID); if (i_mm->t->template) { unlink(i_mm->t->template); free(i_mm->t->template); } } } shmdt(i_mm->t); } else { free(i_mm->t); } #endif if (i_mm->t->path) { munmap(i_mm->t->addr, i_mm->t->len); if (i_mm->t->path != (char *)-1) { if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && truncate(i_mm->t->path, i_mm->t->real) == -1) { free(i_mm->t->path); free(i_mm); rb_raise(rb_eTypeError, "truncate"); } free(i_mm->t->path); } } free(i_mm); } static void mm_lock(i_mm, wait_lock) mm_ipc *i_mm; int wait_lock; { #if HAVE_SEMCTL && HAVE_SHMCTL struct sembuf sem_op; if (i_mm->t->flag & MM_IPC) { i_mm->count++; if (i_mm->count == 1) { retry: sem_op.sem_num = 0; sem_op.sem_op = -1; sem_op.sem_flg = IPC_NOWAIT; if (semop(i_mm->t->semid, &sem_op, 1) == -1) { if (errno == EAGAIN) { if (!wait_lock) { rb_raise(rb_const_get(rb_mErrno, rb_intern("EAGAIN")), "EAGAIN"); } rb_thread_sleep(1); goto retry; } rb_sys_fail("semop()"); } } } #endif } static void mm_unlock(i_mm) mm_ipc *i_mm; { #if HAVE_SEMCTL && HAVE_SHMCTL struct sembuf sem_op; if (i_mm->t->flag & MM_IPC) { i_mm->count--; if (!i_mm->count) { retry: sem_op.sem_num = 0; sem_op.sem_op = 1; sem_op.sem_flg = IPC_NOWAIT; if (semop(i_mm->t->semid, &sem_op, 1) == -1) { if (errno == EAGAIN) { rb_thread_sleep(1); goto retry; } rb_sys_fail("semop()"); } } } #endif } #define GetMmap(obj, i_mm, t_modify) \ Data_Get_Struct(obj, mm_ipc, i_mm); \ if (!i_mm->t->path) { \ rb_raise(rb_eIOError, "unmapped file"); \ } \ if ((t_modify & MM_MODIFY) && (i_mm->t->flag & MM_FROZEN)) { \ rb_error_frozen("mmap"); \ } static VALUE mm_vunlock(obj) VALUE obj; { mm_ipc *i_mm; GetMmap(obj, i_mm, 0); mm_unlock(i_mm); return Qnil; } /* * call-seq: semlock * * Create a lock */ static VALUE mm_semlock(argc, argv, obj) int argc; VALUE *argv, obj; { mm_ipc *i_mm; GetMmap(obj, i_mm, 0); if (!(i_mm->t->flag & MM_IPC)) { rb_warning("useless use of #semlock"); rb_yield(obj); } else { #if HAVE_SEMCTL && HAVE_SHMCTL VALUE a; int wait_lock = Qtrue; if (rb_scan_args(argc, argv, "01", &a)) { wait_lock = RTEST(a); } mm_lock(i_mm, wait_lock); rb_ensure(rb_yield, obj, mm_vunlock, obj); #endif } return Qnil; } /* * call-seq: ipc_key * * Get the ipc key */ static VALUE mm_ipc_key(obj) VALUE obj; { mm_ipc *i_mm; GetMmap(obj, i_mm, 0); if (i_mm->t->flag & MM_IPC) { return INT2NUM(i_mm->t->key); } return INT2NUM(-1); } /* * Document-method: munmap * Document-method: unmap * * call-seq: munmap * * terminate the association */ static VALUE mm_unmap(obj) VALUE obj; { mm_ipc *i_mm; GetMmap(obj, i_mm, 0); if (i_mm->t->path) { mm_lock(i_mm, Qtrue); munmap(i_mm->t->addr, i_mm->t->len); if (i_mm->t->path != (char *)-1) { if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && truncate(i_mm->t->path, i_mm->t->real) == -1) { rb_raise(rb_eTypeError, "truncate"); } free(i_mm->t->path); } i_mm->t->path = NULL; mm_unlock(i_mm); } return Qnil; } /* * call-seq: freeze * * freeze the current file */ static VALUE mm_freeze(obj) VALUE obj; { mm_ipc *i_mm; rb_obj_freeze(obj); GetMmap(obj, i_mm, 0); i_mm->t->flag |= MM_FROZEN; return obj; } static VALUE mm_str(VALUE obj, int modify) { mm_ipc *i_mm; VALUE ret = Qnil; GetMmap(obj, i_mm, modify & ~MM_ORIGIN); if (modify & MM_MODIFY) { if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); if (!OBJ_TAINTED(ret) && rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't modify mmap"); } ret = rb_obj_alloc(rb_cString); if (rb_obj_tainted(obj)) { OBJ_TAINT(ret); } RSTRING(ret)->as.heap.ptr = i_mm->t->addr; RSTRING(ret)->as.heap.aux.capa = i_mm->t->len; RSTRING(ret)->as.heap.len = i_mm->t->real; if (modify & MM_ORIGIN) { #if HAVE_RB_DEFINE_ALLOC_FUNC RSTRING(ret)->as.heap.aux.shared = obj; FL_SET(ret, RSTRING_NOEMBED); FL_SET(ret, FL_USER18); #else RSTRING(ret)->orig = ret; #endif } if (i_mm->t->flag & MM_FROZEN) { ret = rb_obj_freeze(ret); } return ret; } /* * call-seq: to_str * * Convert object to a string */ static VALUE mm_to_str(obj) VALUE obj; { return mm_str(obj, MM_ORIGIN); } extern char *ruby_strdup(); typedef struct { mm_ipc *i_mm; size_t len; } mm_st; static VALUE mm_i_expand(st_mm) mm_st *st_mm; { int fd; mm_ipc *i_mm = st_mm->i_mm; size_t len = st_mm->len; if (munmap(i_mm->t->addr, i_mm->t->len)) { rb_raise(rb_eArgError, "munmap failed"); } if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) { rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path); } if (len > i_mm->t->len) { if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) { rb_raise(rb_eIOError, "Can't lseek %lu", len - i_mm->t->len - 1); } if (write(fd, "\000", 1) != 1) { rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path); } } else if (len < i_mm->t->len && truncate(i_mm->t->path, len) == -1) { rb_raise(rb_eIOError, "Can't truncate %s", i_mm->t->path); } i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset); close(fd); if (i_mm->t->addr == MAP_FAILED) { rb_raise(rb_eArgError, "mmap failed"); } #ifdef MADV_NORMAL if (i_mm->t->advice && madvise(i_mm->t->addr, len, i_mm->t->advice) == -1) { rb_raise(rb_eArgError, "madvise(%d)", errno); } #endif if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) { rb_raise(rb_eArgError, "mlock(%d)", errno); } i_mm->t->len = len; return Qnil; } static void mm_expandf(i_mm, len) mm_ipc *i_mm; size_t len; { int status; mm_st st_mm; if (i_mm->t->vscope == MAP_PRIVATE) { rb_raise(rb_eTypeError, "expand for a private map"); } if (i_mm->t->flag & MM_FIXED) { rb_raise(rb_eTypeError, "expand for a fixed map"); } if (!i_mm->t->path || i_mm->t->path == (char *)-1) { rb_raise(rb_eTypeError, "expand for an anonymous map"); } st_mm.i_mm = i_mm; st_mm.len = len; if (i_mm->t->flag & MM_IPC) { mm_lock(i_mm, Qtrue); rb_protect(mm_i_expand, (VALUE)&st_mm, &status); mm_unlock(i_mm); if (status) { rb_jump_tag(status); } } else { mm_i_expand(&st_mm); } } static void mm_realloc(i_mm, len) mm_ipc *i_mm; size_t len; { if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); if (len > i_mm->t->len) { if ((len - i_mm->t->len) < i_mm->t->incr) { len = i_mm->t->len + i_mm->t->incr; } mm_expandf(i_mm, len); } } /* * call-seq: * extend(count) * * add count bytes to the file (i.e. pre-extend the file) */ static VALUE mm_extend(obj, a) VALUE obj, a; { mm_ipc *i_mm; long len; GetMmap(obj, i_mm, MM_MODIFY); len = NUM2LONG(a); if (len > 0) { mm_expandf(i_mm, i_mm->t->len + len); } return UINT2NUM(i_mm->t->len); } static VALUE mm_i_options(arg, obj) VALUE arg, obj; { mm_ipc *i_mm; char *options; VALUE key, value; Data_Get_Struct(obj, mm_ipc, i_mm); key = rb_ary_entry(arg, 0); value = rb_ary_entry(arg, 1); key = rb_obj_as_string(key); options = StringValuePtr(key); if (strcmp(options, "length") == 0) { i_mm->t->len = NUM2UINT(value); if (i_mm->t->len <= 0) { rb_raise(rb_eArgError, "Invalid value for length %zu", i_mm->t->len); } i_mm->t->flag |= MM_FIXED; } else if (strcmp(options, "offset") == 0) { i_mm->t->offset = NUM2INT(value); if (i_mm->t->offset < 0) { rb_raise(rb_eArgError, "Invalid value for offset %lld", i_mm->t->offset); } i_mm->t->flag |= MM_FIXED; } else if (strcmp(options, "advice") == 0) { i_mm->t->advice = NUM2INT(value); } else if (strcmp(options, "increment") == 0) { int incr = NUM2INT(value); if (incr < 0) { rb_raise(rb_eArgError, "Invalid value for increment %d", incr); } i_mm->t->incr = incr; } else if (strcmp(options, "initialize") == 0) { } #if HAVE_SEMCTL && HAVE_SHMCTL else if (strcmp(options, "ipc") == 0) { if (value != Qtrue && TYPE(value) != T_HASH) { rb_raise(rb_eArgError, "Expected an Hash for :ipc"); } i_mm->t->shmid = value; i_mm->t->flag |= (MM_IPC | MM_TMP); } #endif else { rb_warning("Unknown option `%s'", options); } return Qnil; } #if HAVE_SEMCTL && HAVE_SHMCTL static VALUE mm_i_ipc(arg, obj) VALUE arg, obj; { mm_ipc *i_mm; char *options; VALUE key, value; Data_Get_Struct(obj, mm_ipc, i_mm); key = rb_ary_entry(arg, 0); value = rb_ary_entry(arg, 1); key = rb_obj_as_string(key); options = StringValuePtr(key); if (strcmp(options, "key") == 0) { i_mm->t->key = rb_funcall2(value, rb_intern("to_int"), 0, 0); } else if (strcmp(options, "permanent") == 0) { if (RTEST(value)) { i_mm->t->flag &= ~MM_TMP; } } else if (strcmp(options, "mode") == 0) { i_mm->t->semid = NUM2INT(value); } else { rb_warning("Unknown option `%s'", options); } return Qnil; } #endif /* * call-seq: * new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {}) * * create a new Mmap object * * * file * * Pathname of the file, if nil is given an anonymous map * is created Mmanp::MAP_ANON * * * mode * * Mode to open the file, it can be "r", "w", "rw", "a" * * * protection * * specify the nature of the mapping * * * Mmap::MAP_SHARED * * Creates a mapping that's shared with all other processes * mapping the same areas of the file. * The default value is Mmap::MAP_SHARED * * * Mmap::MAP_PRIVATE * * Creates a private copy-on-write mapping, so changes to the * contents of the mmap object will be private to this process * * * options * * Hash. If one of the options length or offset * is specified it will not possible to modify the size of * the mapped file. * * length:: maps length bytes from the file * * offset:: the mapping begin at offset * * advice:: the type of the access (see #madvise) */ static VALUE mm_s_new(argc, argv, obj) int argc; VALUE *argv, obj; { VALUE res = rb_funcall2(obj, rb_intern("allocate"), 0, 0); rb_obj_call_init(res, argc, argv); return res; } static VALUE mm_s_alloc(obj) VALUE obj; { VALUE res; mm_ipc *i_mm; res = Data_Make_Struct(obj, mm_ipc, 0, mm_free, i_mm); i_mm->t = ALLOC_N(mm_mmap, 1); MEMZERO(i_mm->t, mm_mmap, 1); i_mm->t->incr = EXP_INCR_SIZE; return res; } /* * call-seq: initialize * * Create a new Mmap object */ static VALUE mm_init(argc, argv, obj) VALUE obj, *argv; int argc; { struct stat st; int fd, smode = 0, pmode = 0, vscope, perm, init; MMAP_RETTYPE addr; VALUE fname, fdv, vmode, scope, options; mm_ipc *i_mm; char *path, *mode; size_t size = 0; off_t offset; int anonymous; options = Qnil; if (argc > 1 && TYPE(argv[argc - 1]) == T_HASH) { options = argv[argc - 1]; argc--; } rb_scan_args(argc, argv, "12", &fname, &vmode, &scope); vscope = 0; path = 0; fd = -1; anonymous = 0; fdv = Qnil; #ifdef MAP_ANON if (NIL_P(fname)) { vscope = MAP_ANON | MAP_SHARED; anonymous = 1; } else #endif { if (rb_safe_level() > 0 && OBJ_TAINTED(fname)){ rb_raise(rb_eSecurityError, "Insecure operation"); } rb_secure(4); if (rb_respond_to(fname, rb_intern("fileno"))) { fdv = rb_funcall2(fname, rb_intern("fileno"), 0, 0); } if (NIL_P(fdv)) { fname = rb_str_to_str(fname); SafeStringValue(fname); path = StringValuePtr(fname); } else { fd = NUM2INT(fdv); if (fd < 0) { rb_raise(rb_eArgError, "invalid file descriptor %d", fd); } } if (!NIL_P(scope)) { vscope = NUM2INT(scope); #ifdef MAP_ANON if (vscope & MAP_ANON) { rb_raise(rb_eArgError, "filename specified for an anonymous map"); } #endif } } vscope |= NIL_P(scope) ? MAP_SHARED : NUM2INT(scope); size = 0; perm = 0666; if (!anonymous) { if (NIL_P(vmode)) { mode = "r"; } else if (rb_respond_to(vmode, rb_intern("to_ary"))) { VALUE tmp; vmode = rb_convert_type(vmode, T_ARRAY, "Array", "to_ary"); if (RARRAY_LEN(vmode) != 2) { rb_raise(rb_eArgError, "Invalid length %ld (expected 2)", RARRAY_LEN(vmode)); } tmp = rb_ary_entry(vmode, 0); mode = StringValuePtr(tmp); perm = NUM2INT(rb_ary_entry(vmode, 1)); } else { mode = StringValuePtr(vmode); } if (strcmp(mode, "r") == 0) { smode = O_RDONLY; pmode = PROT_READ; } else if (strcmp(mode, "w") == 0) { smode = O_RDWR | O_TRUNC; pmode = PROT_READ | PROT_WRITE; } else if (strcmp(mode, "rw") == 0 || strcmp(mode, "wr") == 0) { smode = O_RDWR; pmode = PROT_READ | PROT_WRITE; } else if (strcmp(mode, "a") == 0) { smode = O_RDWR | O_CREAT; pmode = PROT_READ | PROT_WRITE; } else { rb_raise(rb_eArgError, "Invalid mode %s", mode); } if (NIL_P(fdv)) { if ((fd = open(path, smode, perm)) == -1) { rb_raise(rb_eArgError, "Can't open %s", path); } } if (fstat(fd, &st) == -1) { rb_raise(rb_eArgError, "Can't stat %s", path); } size = st.st_size; } else { fd = -1; if (!NIL_P(vmode) && TYPE(vmode) != T_STRING) { size = NUM2INT(vmode); } } Data_Get_Struct(obj, mm_ipc, i_mm); if (i_mm->t->flag & MM_FROZEN) { rb_raise(rb_eArgError, "frozen mmap"); } i_mm->t->shmid = 0; i_mm->t->semid = 0; offset = 0; if (options != Qnil) { rb_iterate(rb_each, options, mm_i_options, obj); if (path && (i_mm->t->len + i_mm->t->offset) > st.st_size) { rb_raise(rb_eArgError, "invalid value for length (%ld) or offset (%lld)", i_mm->t->len, i_mm->t->offset); } if (i_mm->t->len) size = i_mm->t->len; offset = i_mm->t->offset; #if HAVE_SEMCTL && HAVE_SHMCTL if (i_mm->t->flag & MM_IPC) { key_t key; int shmid, semid, mode; union semun sem_val; struct shmid_ds buf; mm_mmap *data; if (!(vscope & MAP_SHARED)) { rb_warning("Probably it will not do what you expect ..."); } i_mm->t->key = -1; i_mm->t->semid = 0; if (TYPE(i_mm->t->shmid) == T_HASH) { rb_iterate(rb_each, i_mm->t->shmid, mm_i_ipc, obj); } i_mm->t->shmid = 0; if (i_mm->t->semid) { mode = i_mm->t->semid; i_mm->t->semid = 0; } else { mode = 0644; } if ((int)i_mm->t->key <= 0) { mode |= IPC_CREAT; strcpy(template, "/tmp/ruby_mmap.XXXXXX"); if (mkstemp(template) == -1) { rb_sys_fail("mkstemp()"); } if ((key = ftok(template, 'R')) == -1) { rb_sys_fail("ftok()"); } } else { key = (key_t)i_mm->t->key; } if ((shmid = shmget(key, sizeof(mm_ipc), mode)) == -1) { rb_sys_fail("shmget()"); } data = shmat(shmid, (void *)0, 0); if (data == (mm_mmap *)-1) { rb_sys_fail("shmat()"); } if (i_mm->t->flag & MM_TMP) { if (shmctl(shmid, IPC_RMID, &buf) == -1) { rb_sys_fail("shmctl()"); } } if ((semid = semget(key, 1, mode)) == -1) { rb_sys_fail("semget()"); } if (mode & IPC_CREAT) { sem_val.val = 1; if (semctl(semid, 0, SETVAL, sem_val) == -1) { rb_sys_fail("semctl()"); } } memcpy(data, i_mm->t, sizeof(mm_mmap)); free(i_mm->t); i_mm->t = data; i_mm->t->key = key; i_mm->t->semid = semid; i_mm->t->shmid = shmid; if (i_mm->t->flag & MM_TMP) { i_mm->t->template = ALLOC_N(char, strlen(template) + 1); strcpy(i_mm->t->template, template); } } #endif } init = 0; if (anonymous) { if (size <= 0) { rb_raise(rb_eArgError, "length not specified for an anonymous map"); } if (offset) { rb_warning("Ignoring offset for an anonymous map"); offset = 0; } smode = O_RDWR; pmode = PROT_READ | PROT_WRITE; i_mm->t->flag |= MM_FIXED | MM_ANON; } else { if (size == 0 && (smode & O_RDWR)) { if (lseek(fd, i_mm->t->incr - 1, SEEK_END) == -1) { rb_raise(rb_eIOError, "Can't lseek %lu", i_mm->t->incr - 1); } if (write(fd, "\000", 1) != 1) { rb_raise(rb_eIOError, "Can't extend %s", path); } init = 1; size = i_mm->t->incr; } if (!NIL_P(fdv)) { i_mm->t->flag |= MM_FIXED; } } addr = mmap(0, size, pmode, vscope, fd, offset); if (NIL_P(fdv) && !anonymous) { close(fd); } if (addr == MAP_FAILED || !addr) { rb_raise(rb_eArgError, "mmap failed (%d)", errno); } #ifdef MADV_NORMAL if (i_mm->t->advice && madvise(addr, size, i_mm->t->advice) == -1) { rb_raise(rb_eArgError, "madvise(%d)", errno); } #endif if (anonymous && TYPE(options) == T_HASH) { VALUE val; char *ptr; val = rb_hash_aref(options, rb_str_new2("initialize")); if (!NIL_P(val)) { ptr = StringValuePtr(val); memset(addr, ptr[0], size); } } i_mm->t->addr = addr; i_mm->t->len = size; if (!init) i_mm->t->real = size; i_mm->t->pmode = pmode; i_mm->t->vscope = vscope; i_mm->t->smode = smode & ~O_TRUNC; i_mm->t->path = (path)?ruby_strdup(path):(char *)-1; if (smode == O_RDONLY) { obj = rb_obj_freeze(obj); i_mm->t->flag |= MM_FROZEN; } else { if (smode == O_WRONLY) { i_mm->t->flag |= MM_FIXED; } OBJ_TAINT(obj); } return obj; } /* * Document-method: msync * Document-method: sync * Document-method: flush * * call-seq: msync * * flush the file */ static VALUE mm_msync(argc, argv, obj) int argc; VALUE *argv, obj; { mm_ipc *i_mm; VALUE oflag; int ret; int flag = MS_SYNC; if (argc) { rb_scan_args(argc, argv, "01", &oflag); flag = NUM2INT(oflag); } GetMmap(obj, i_mm, MM_MODIFY); if ((ret = msync(i_mm->t->addr, i_mm->t->len, flag)) != 0) { rb_raise(rb_eArgError, "msync(%d)", ret); } if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE) mm_expandf(i_mm, i_mm->t->real); return obj; } /* * Document-method: mprotect * Document-method: protect * * call-seq: mprotect(mode) * * change the mode, value must be "r", "w" or "rw" */ static VALUE mm_mprotect(obj, a) VALUE obj, a; { mm_ipc *i_mm; int ret, pmode; char *smode; GetMmap(obj, i_mm, 0); if (TYPE(a) == T_STRING) { smode = StringValuePtr(a); if (strcmp(smode, "r") == 0) pmode = PROT_READ; else if (strcmp(smode, "w") == 0) pmode = PROT_WRITE; else if (strcmp(smode, "rw") == 0 || strcmp(smode, "wr") == 0) pmode = PROT_READ | PROT_WRITE; else { rb_raise(rb_eArgError, "Invalid mode %s", smode); } } else { pmode = NUM2INT(a); } if ((pmode & PROT_WRITE) && (i_mm->t->flag & MM_FROZEN)) rb_error_frozen("mmap"); if ((ret = mprotect(i_mm->t->addr, i_mm->t->len, pmode | PROT_READ)) != 0) { rb_raise(rb_eArgError, "mprotect(%d)", ret); } i_mm->t->pmode = pmode; if (pmode & PROT_READ) { if (pmode & PROT_WRITE) i_mm->t->smode = O_RDWR; else { i_mm->t->smode = O_RDONLY; obj = rb_obj_freeze(obj); i_mm->t->flag |= MM_FROZEN; } } else if (pmode & PROT_WRITE) { i_mm->t->flag |= MM_FIXED; i_mm->t->smode = O_WRONLY; } return obj; } #ifdef MADV_NORMAL /* * Document-method: madvise * Document-method: advise * * call-seq: madvise(advice) * * advice can have the value Mmap::MADV_NORMAL, * Mmap::MADV_RANDOM, Mmap::MADV_SEQUENTIAL, * Mmap::MADV_WILLNEED, Mmap::MADV_DONTNEED * */ static VALUE mm_madvise(obj, a) VALUE obj, a; { mm_ipc *i_mm; GetMmap(obj, i_mm, 0); if (madvise(i_mm->t->addr, i_mm->t->len, NUM2INT(a)) == -1) { rb_raise(rb_eTypeError, "madvise(%d)", errno); } i_mm->t->advice = NUM2INT(a); return Qnil; } #endif #define StringMmap(b, bp, bl) \ do { \ if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ mm_ipc *b_mm; \ GetMmap(b, b_mm, 0); \ bp = b_mm->t->addr; \ bl = b_mm->t->real; \ } \ else { \ bp = StringValuePtr(b); \ bl = RSTRING_LEN(b); \ } \ } while (0); static void mm_update(str, beg, len, val) mm_ipc *str; VALUE val; long beg; long len; { char *valp; long vall; if (str->t->flag & MM_FROZEN) rb_error_frozen("mmap"); if (len < 0) rb_raise(rb_eIndexError, "negative length %ld", len); mm_lock(str, Qtrue); if (beg < 0) { beg += str->t->real; } if (beg < 0 || str->t->real < (size_t)beg) { if (beg < 0) { beg -= str->t->real; } mm_unlock(str); rb_raise(rb_eIndexError, "index %ld out of string", beg); } if (str->t->real < (size_t)(beg + len)) { len = str->t->real - beg; } mm_unlock(str); StringMmap(val, valp, vall); mm_lock(str, Qtrue); if ((str->t->flag & MM_FIXED) && vall != len) { mm_unlock(str); rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } if (len < vall) { mm_realloc(str, str->t->real + vall - len); } if (vall != len) { memmove((char *)str->t->addr + beg + vall, (char *)str->t->addr + beg + len, str->t->real - (beg + len)); } if (str->t->real < (size_t)beg && len < 0) { MEMZERO(str->t->addr + str->t->real, char, -len); } if (vall > 0) { memmove((char *)str->t->addr + beg, valp, vall); } str->t->real += vall - len; mm_unlock(str); } /* * call-seq: =~(other) * * return an index of the match */ static VALUE mm_match(x, y) VALUE x, y; { VALUE reg, res; long start; x = mm_str(x, MM_ORIGIN); if (TYPE(y) == T_DATA && RDATA(y)->dfree == (RUBY_DATA_FUNC)mm_free) { y = mm_to_str(y); } switch (TYPE(y)) { case T_REGEXP: res = rb_reg_match(y, x); break; case T_STRING: reg = rb_reg_regcomp(y); start = rb_reg_search(reg, x, 0, 0); if (start == -1) res = Qnil; else res = INT2NUM(start); break; default: res = rb_funcall(y, rb_intern("=~"), 1, x); break; } return res; } static VALUE get_pat(pat) VALUE pat; { switch (TYPE(pat)) { case T_REGEXP: break; case T_STRING: pat = rb_reg_regcomp(pat); break; default: /* type failed */ Check_Type(pat, T_REGEXP); } return pat; } static int mm_correct_backref() { VALUE match; int i, start; match = rb_backref_get(); if (NIL_P(match)) return 0; if (RMATCH(match)->BEG(0) == -1) return 0; start = RMATCH(match)->BEG(0); RMATCH(match)->str = rb_str_new(StringValuePtr(RMATCH(match)->str) + start, RMATCH(match)->END(0) - start); if (OBJ_TAINTED(match)) OBJ_TAINT(RMATCH(match)->str); for (i = 0; i < RMATCH(match)->rmatch->regs.num_regs && RMATCH(match)->BEG(i) != -1; i++) { RMATCH(match)->BEG(i) -= start; RMATCH(match)->END(i) -= start; } rb_backref_set(match); return start; } static VALUE mm_sub_bang_int(bang_st) mm_bang *bang_st; { int argc = bang_st->argc; VALUE *argv = bang_st->argv; VALUE obj = bang_st->obj; VALUE pat, repl = Qnil, match, str, res; struct re_registers *regs; int start, iter = 0; int tainted = 0; long plen; mm_ipc *i_mm; if (argc == 1 && rb_block_given_p()) { iter = 1; } else if (argc == 2) { repl = rb_str_to_str(argv[1]); if (OBJ_TAINTED(repl)) tainted = 1; } else { rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); } GetMmap(obj, i_mm, MM_MODIFY); str = mm_str(obj, MM_MODIFY | MM_ORIGIN); pat = get_pat(argv[0]); res = Qnil; if (rb_reg_search(pat, str, 0, 0) >= 0) { start = mm_correct_backref(); match = rb_backref_get(); regs = &RMATCH(match)->rmatch->regs; if (iter) { rb_match_busy(match); repl = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); rb_backref_set(match); } else { RSTRING(str)->as.heap.ptr += start; repl = rb_reg_regsub(repl, str, regs, match); RSTRING(str)->as.heap.ptr -= start; } if (OBJ_TAINTED(repl)) tainted = 1; plen = RMATCH(match)->END(0) - RMATCH(match)->BEG(0); if (RSTRING_LEN(repl) > plen) { mm_realloc(i_mm, RSTRING_LEN(str) + RSTRING_LEN(repl) - plen); RSTRING(str)->as.heap.ptr = i_mm->t->addr; } if (RSTRING_LEN(repl) != plen) { if (i_mm->t->flag & MM_FIXED) { rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } memmove(RSTRING_PTR(str) + start + RMATCH(match)->BEG(0) + RSTRING_LEN(repl), RSTRING_PTR(str) + start + RMATCH(match)->BEG(0) + plen, RSTRING_LEN(str) - start - RMATCH(match)->BEG(0) - plen); } memcpy(RSTRING_PTR(str) + start + RMATCH(match)->BEG(0), RSTRING_PTR(repl), RSTRING_LEN(repl)); i_mm->t->real += RSTRING_LEN(repl) - plen; if (tainted) OBJ_TAINT(obj); res = obj; } rb_gc_force_recycle(str); return res; } /* * call-seq: * str.sub!(pattern, replacement) => str or nil * str.sub!(pattern) {|match| block } => str or nil * * substitution */ static VALUE mm_sub_bang(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { VALUE res; mm_bang bang_st; mm_ipc *i_mm; bang_st.argc = argc; bang_st.argv = argv; bang_st.obj = obj; GetMmap(obj, i_mm, MM_MODIFY); if (i_mm->t->flag & MM_IPC) { mm_lock(i_mm, Qtrue); res = rb_ensure(mm_sub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); } else { res = mm_sub_bang_int(&bang_st); } return res; } static VALUE mm_gsub_bang_int(bang_st) mm_bang *bang_st; { int argc = bang_st->argc; VALUE *argv = bang_st->argv; VALUE obj = bang_st->obj; VALUE pat, val, repl = Qnil, match, str; struct re_registers *regs; long beg, offset; int start, iter = 0; int tainted = 0; long plen; mm_ipc *i_mm; if (argc == 1 && rb_block_given_p()) { iter = 1; } else if (argc == 2) { repl = rb_str_to_str(argv[1]); if (OBJ_TAINTED(repl)) tainted = 1; } else { rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); } GetMmap(obj, i_mm, MM_MODIFY); str = mm_str(obj, MM_MODIFY | MM_ORIGIN); pat = get_pat(argv[0]); offset = 0; beg = rb_reg_search(pat, str, 0, 0); if (beg < 0) { rb_gc_force_recycle(str); return Qnil; } while (beg >= 0) { start = mm_correct_backref(); match = rb_backref_get(); regs = &RMATCH(match)->rmatch->regs; if (iter) { rb_match_busy(match); val = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); rb_backref_set(match); } else { RSTRING(str)->as.heap.ptr += start; val = rb_reg_regsub(repl, str, regs, match); RSTRING(str)->as.heap.ptr -= start; } if (OBJ_TAINTED(repl)) tainted = 1; plen = RMATCH(match)->END(0) - RMATCH(match)->BEG(0); if ((i_mm->t->real + RSTRING_LEN(val) - plen) > i_mm->t->len) { mm_realloc(i_mm, RSTRING_LEN(str) + RSTRING_LEN(val) - plen); } if (RSTRING_LEN(val) != plen) { if (i_mm->t->flag & MM_FIXED) { rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } memmove(RSTRING_PTR(str) + start + RMATCH(match)->BEG(0) + RSTRING_LEN(val), RSTRING_PTR(str) + start + RMATCH(match)->BEG(0) + plen, RSTRING_LEN(str) - start - RMATCH(match)->BEG(0) - plen); } memcpy(RSTRING_PTR(str) + start + RMATCH(match)->BEG(0), RSTRING_PTR(val), RSTRING_LEN(val)); RSTRING(str)->as.heap.len += RSTRING_LEN(val) - plen; i_mm->t->real = RSTRING_LEN(str); if (RMATCH(match)->BEG(0) == RMATCH(match)->END(0)) { offset = start + RMATCH(match)->END(0) + 0; // TODO: fix mm_gsub_bang_int // mbclen2(RSTRING_PTR(str)[RMATCH(match)->END(0)], pat); offset += RSTRING_LEN(val) - plen; } else { offset = start + RMATCH(match)->END(0) + RSTRING_LEN(val) - plen; } if (offset > RSTRING_LEN(str)) break; beg = rb_reg_search(pat, str, offset, 0); } rb_backref_set(match); if (tainted) OBJ_TAINT(obj); rb_gc_force_recycle(str); return obj; } /* * call-seq: * str.gsub!(pattern, replacement) => str or nil * str.gsub!(pattern) {|match| block } => str or nil * * global substitution */ static VALUE mm_gsub_bang(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { VALUE res; mm_bang bang_st; mm_ipc *i_mm; bang_st.argc = argc; bang_st.argv = argv; bang_st.obj = obj; GetMmap(obj, i_mm, MM_MODIFY); if (i_mm->t->flag & MM_IPC) { mm_lock(i_mm, Qtrue); res = rb_ensure(mm_gsub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); } else { res = mm_gsub_bang_int(&bang_st); } return res; } static VALUE mm_index __((int, VALUE *, VALUE)); #if HAVE_RB_DEFINE_ALLOC_FUNC static void mm_subpat_set(obj, re, offset, val) VALUE obj, re; int offset; VALUE val; { VALUE str, match; int start, end, len; mm_ipc *i_mm; str = mm_str(obj, MM_MODIFY | MM_ORIGIN); if (rb_reg_search(re, str, 0, 0) < 0) { rb_raise(rb_eIndexError, "regexp not matched"); } match = rb_backref_get(); if (offset >= RMATCH(match)->rmatch->regs.num_regs) { rb_raise(rb_eIndexError, "index %d out of regexp", offset); } start = RMATCH(match)->BEG(offset); if (start == -1) { rb_raise(rb_eIndexError, "regexp group %d not matched", offset); } end = RMATCH(match)->END(offset); len = end - start; GetMmap(obj, i_mm, MM_MODIFY); mm_update(i_mm, start, len, val); } #endif static VALUE mm_aset(str, indx, val) VALUE str; VALUE indx, val; { long idx; mm_ipc *i_mm; GetMmap(str, i_mm, MM_MODIFY); switch (TYPE(indx)) { case T_FIXNUM: num_index: idx = NUM2INT(indx); if (idx < 0) { idx += i_mm->t->real; } if (idx < 0 || i_mm->t->real <= (size_t)idx) { rb_raise(rb_eIndexError, "index %ld out of string", idx); } if (FIXNUM_P(val)) { if (i_mm->t->real == (size_t)idx) { i_mm->t->real += 1; mm_realloc(i_mm, i_mm->t->real); } ((char *)i_mm->t->addr)[idx] = NUM2INT(val) & 0xff; } else { mm_update(i_mm, idx, 1, val); } return val; case T_REGEXP: #if HAVE_RB_DEFINE_ALLOC_FUNC mm_subpat_set(str, indx, 0, val); #else { VALUE args[2]; args[0] = indx; args[1] = val; mm_sub_bang(2, args, str); } #endif return val; case T_STRING: { VALUE res; res = mm_index(1, &indx, str); if (!NIL_P(res)) { mm_update(i_mm, NUM2LONG(res), RSTRING_LEN(indx), val); } return val; } default: /* check if indx is Range */ { long beg, len; if (rb_range_beg_len(indx, &beg, &len, i_mm->t->real, 2)) { mm_update(i_mm, beg, len, val); return val; } } idx = NUM2LONG(indx); goto num_index; } } /* * call-seq: []=(args) * * Element assignement - with the following syntax * * self[nth] = val * * change the nth character with val * * self[start..last] = val * * change substring from start to last with val * * self[start, len] = val * * replace length characters from start with val. * */ static VALUE mm_aset_m(argc, argv, str) int argc; VALUE *argv; VALUE str; { mm_ipc *i_mm; GetMmap(str, i_mm, MM_MODIFY); if (argc == 3) { long beg, len; #if HAVE_RB_DEFINE_ALLOC_FUNC if (TYPE(argv[0]) == T_REGEXP) { mm_subpat_set(str, argv[0], NUM2INT(argv[1]), argv[2]); } else #endif { beg = NUM2INT(argv[0]); len = NUM2INT(argv[1]); mm_update(i_mm, beg, len, argv[2]); } return argv[2]; } if (argc != 2) { rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); } return mm_aset(str, argv[0], argv[1]); } #if HAVE_RB_STR_INSERT /* * call-seq: insert(index, str) * * insert str at index */ static VALUE mm_insert(str, idx, str2) VALUE str, idx, str2; { mm_ipc *i_mm; long pos = NUM2LONG(idx); GetMmap(str, i_mm, MM_MODIFY); if (pos == -1) { pos = RSTRING_LEN(str); } else if (pos < 0) { pos++; } mm_update(i_mm, pos, 0, str2); return str; } #endif static VALUE mm_aref_m _((int, VALUE *, VALUE)); /* * call-seq: slice!(str) * * delete the specified portion of the file */ static VALUE mm_slice_bang(argc, argv, str) int argc; VALUE *argv; VALUE str; { VALUE result; VALUE buf[3]; int i; if (argc < 1 || 2 < argc) { rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); } for (i = 0; i < argc; i++) { buf[i] = argv[i]; } buf[i] = rb_str_new(0,0); result = mm_aref_m(argc, buf, str); if (!NIL_P(result)) { mm_aset_m(argc+1, buf, str); } return result; } static VALUE mm_cat(str, ptr, len) VALUE str; const char *ptr; long len; { mm_ipc *i_mm; char *sptr; GetMmap(str, i_mm, MM_MODIFY); if (len > 0) { int poffset = -1; sptr = (char *)i_mm->t->addr; if (sptr <= ptr && ptr < sptr + i_mm->t->real) { poffset = ptr - sptr; } mm_lock(i_mm, Qtrue); mm_realloc(i_mm, i_mm->t->real + len); sptr = (char *)i_mm->t->addr; if (ptr) { if (poffset >= 0) ptr = sptr + poffset; memcpy(sptr + i_mm->t->real, ptr, len); } i_mm->t->real += len; mm_unlock(i_mm); } return str; } static VALUE mm_append(str1, str2) VALUE str1, str2; { str2 = rb_str_to_str(str2); str1 = mm_cat(str1, StringValuePtr(str2), RSTRING_LEN(str2)); return str1; } /* * Document-method: concat * Document-method: << * * call-seq: concat(other) * * append the contents of other */ static VALUE mm_concat(str1, str2) VALUE str1, str2; { if (FIXNUM_P(str2)) { int i = FIX2INT(str2); if (0 <= i && i <= 0xff) { /* byte */ char c = i; return mm_cat(str1, &c, 1); } } str1 = mm_append(str1, str2); return str1; } #ifndef HAVE_RB_STR_LSTRIP /* * call-seq: strip! * * removes leading and trailing whitespace */ static VALUE mm_strip_bang(str) VALUE str; { char *s, *t, *e; mm_ipc *i_mm; GetMmap(str, i_mm, MM_MODIFY); mm_lock(i_mm, Qtrue); s = (char *)i_mm->t->addr; e = t = s + i_mm->t->real; while (s < t && ISSPACE(*s)) s++; t--; while (s <= t && ISSPACE(*t)) t--; t++; if (i_mm->t->real != (t - s) && (i_mm->t->flag & MM_FIXED)) { mm_unlock(i_mm); rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } i_mm->t->real = t-s; if (s > (char *)i_mm->t->addr) { memmove(i_mm->t->addr, s, i_mm->t->real); ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; } else if (t < e) { ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; } else { str = Qnil; } mm_unlock(i_mm); return str; } #else /* * call-seq: lstrip! * * removes leading whitespace */ static VALUE mm_lstrip_bang(str) VALUE str; { char *s, *t, *e; mm_ipc *i_mm; GetMmap(str, i_mm, MM_MODIFY); mm_lock(i_mm, Qtrue); s = (char *)i_mm->t->addr; e = t = s + i_mm->t->real; while (s < t && ISSPACE(*s)) s++; if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { mm_unlock(i_mm); rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } i_mm->t->real = t - s; if (s > (char *)i_mm->t->addr) { memmove(i_mm->t->addr, s, i_mm->t->real); ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; mm_unlock(i_mm); return str; } mm_unlock(i_mm); return Qnil; } /* * call-seq: rstrip! * * removes trailing whitespace */ static VALUE mm_rstrip_bang(str) VALUE str; { char *s, *t, *e; mm_ipc *i_mm; GetMmap(str, i_mm, MM_MODIFY); mm_lock(i_mm, Qtrue); s = (char *)i_mm->t->addr; e = t = s + i_mm->t->real; t--; while (s <= t && ISSPACE(*t)) t--; t++; if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { mm_unlock(i_mm); rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } i_mm->t->real = t - s; if (t < e) { ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; mm_unlock(i_mm); return str; } mm_unlock(i_mm); return Qnil; } static VALUE mm_strip_bang(str) VALUE str; { VALUE l = mm_lstrip_bang(str); VALUE r = mm_rstrip_bang(str); if (NIL_P(l) && NIL_P(r)) return Qnil; return str; } #endif #define MmapStr(b, recycle) \ do { \ recycle = 0; \ if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ recycle = 1; \ b = mm_str(b, MM_ORIGIN); \ } \ else { \ b = rb_str_to_str(b); \ } \ } while (0); /* * call-seq: <=>(other) * * comparison : return -1, 0, 1 */ static VALUE mm_cmp(a, b) VALUE a, b; { int result; int recycle = 0; a = mm_str(a, MM_ORIGIN); MmapStr(b, recycle); result = rb_str_cmp(a, b); rb_gc_force_recycle(a); if (recycle) rb_gc_force_recycle(b); return INT2FIX(result); } #if HAVE_RB_STR_CASECMP /* * call-seq: casecmp(other) * * only with ruby >= 1.7.1 */ static VALUE mm_casecmp(a, b) VALUE a, b; { VALUE result; int recycle = 0; a = mm_str(a, MM_ORIGIN); MmapStr(b, recycle); result = rb_funcall2(a, rb_intern("casecmp"), 1, &b); rb_gc_force_recycle(a); if (recycle) rb_gc_force_recycle(b); return result; } #endif /* * Document-method: == * Document-method: === * * call-seq: == * * comparison */ static VALUE mm_equal(a, b) VALUE a, b; { VALUE result; mm_ipc *i_mm, *u_mm; if (a == b) return Qtrue; if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) return Qfalse; GetMmap(a, i_mm, 0); GetMmap(b, u_mm, 0); if (i_mm->t->real != u_mm->t->real) return Qfalse; a = mm_str(a, MM_ORIGIN); b = mm_str(b, MM_ORIGIN); result = rb_funcall2(a, rb_intern("=="), 1, &b); rb_gc_force_recycle(a); rb_gc_force_recycle(b); return result; } /* * call-seq: eql?(other) * * Is this eql? to +other+ ? */ static VALUE mm_eql(a, b) VALUE a, b; { VALUE result; mm_ipc *i_mm, *u_mm; if (a == b) return Qtrue; if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) return Qfalse; GetMmap(a, i_mm, 0); GetMmap(b, u_mm, 0); if (i_mm->t->real != u_mm->t->real) return Qfalse; a = mm_str(a, MM_ORIGIN); b = mm_str(b, MM_ORIGIN); result = rb_funcall2(a, rb_intern("eql?"), 1, &b); rb_gc_force_recycle(a); rb_gc_force_recycle(b); return result; } /* * call-seq: hash * * Get the hash value */ static VALUE mm_hash(a) VALUE a; { VALUE b; int res; b = mm_str(a, MM_ORIGIN); res = rb_str_hash(b); rb_gc_force_recycle(b); return INT2FIX(res); } /* * Document-method: length * Document-method: size * * return the size of the file */ static VALUE mm_size(a) VALUE a; { mm_ipc *i_mm; GetMmap(a, i_mm, 0); return UINT2NUM(i_mm->t->real); } /* * call-seq: empty? * * return true if the file is empty */ static VALUE mm_empty(a) VALUE a; { mm_ipc *i_mm; GetMmap(a, i_mm, 0); if (i_mm->t->real == 0) return Qtrue; return Qfalse; } static VALUE mm_protect_bang(t) VALUE *t; { return rb_funcall2(t[0], (ID)t[1], (int)t[2], (VALUE *)t[3]); } static VALUE mm_recycle(str) VALUE str; { rb_gc_force_recycle(str); return str; } static VALUE mm_i_bang(bang_st) mm_bang *bang_st; { VALUE str, res; mm_ipc *i_mm; str = mm_str(bang_st->obj, bang_st->flag); if (bang_st->flag & MM_PROTECT) { VALUE tmp[4]; tmp[0] = str; tmp[1] = (VALUE)bang_st->id; tmp[2] = (VALUE)bang_st->argc; tmp[3] = (VALUE)bang_st->argv; res = rb_ensure(mm_protect_bang, (VALUE)tmp, mm_recycle, str); } else { res = rb_funcall2(str, bang_st->id, bang_st->argc, bang_st->argv); RB_GC_GUARD(res); } if (res != Qnil) { GetMmap(bang_st->obj, i_mm, 0); i_mm->t->real = RSTRING_LEN(str); } return res; } static VALUE mm_bang_i(VALUE obj, int flag, ID id, int argc, VALUE *argv) { VALUE res; mm_ipc *i_mm; mm_bang bang_st; GetMmap(obj, i_mm, 0); if ((flag & MM_CHANGE) && (i_mm->t->flag & MM_FIXED)) { rb_raise(rb_eTypeError, "try to change the size of a fixed map"); } bang_st.obj = obj; bang_st.flag = flag; bang_st.id = id; bang_st.argc = argc; bang_st.argv = argv; if (i_mm->t->flag & MM_IPC) { mm_lock(i_mm, Qtrue); res = rb_ensure(mm_i_bang, (VALUE)&bang_st, mm_vunlock, obj); } else { res = mm_i_bang(&bang_st); } if (res == Qnil) return res; return (flag & MM_ORIGIN)?res:obj; } #if HAVE_RB_STR_MATCH /* * call-seq: match(pattern) * * convert pattern to a Regexp and then call * match on self */ static VALUE mm_match_m(a, b) VALUE a, b; { return mm_bang_i(a, MM_ORIGIN, rb_intern("match"), 1, &b); } #endif /* * call-seq: upcase! * * replaces all lowercase characters to downcase characters */ static VALUE mm_upcase_bang(a) VALUE a; { return mm_bang_i(a, MM_MODIFY, rb_intern("upcase!"), 0, 0); } /* * call-seq: downcase! * * change all uppercase character to lowercase character */ static VALUE mm_downcase_bang(a) VALUE a; { return mm_bang_i(a, MM_MODIFY, rb_intern("downcase!"), 0, 0); } /* * call-seq: capitalize! * * change the first character to uppercase letter */ static VALUE mm_capitalize_bang(a) VALUE a; { return mm_bang_i(a, MM_MODIFY, rb_intern("capitalize!"), 0, 0); } /* * call-seq: swapcase! * * replaces all lowercase characters to uppercase characters, and vice-versa */ static VALUE mm_swapcase_bang(a) VALUE a; { return mm_bang_i(a, MM_MODIFY, rb_intern("swapcase!"), 0, 0); } /* * call-seq: reverse! * * reverse the content of the file */ static VALUE mm_reverse_bang(a) VALUE a; { return mm_bang_i(a, MM_MODIFY, rb_intern("reverse!"), 0, 0); } /* * call-seq: chop! * * chop off the last character */ static VALUE mm_chop_bang(a) VALUE a; { return mm_bang_i(a, MM_CHANGE, rb_intern("chop!"), 0, 0); } /* * call-seq: chomp!(rs = $/) * * chop off the line ending character, specified by rs */ static VALUE mm_chomp_bang(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("chomp!"), argc, argv); } /* * call-seq: delete!(str) * * delete every characters included in str */ static VALUE mm_delete_bang(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("delete!"), argc, argv); } /* * squeeze!(str) * * squeezes sequences of the same characters which is included in str */ static VALUE mm_squeeze_bang(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("squeeze!"), argc, argv); } /* * call-seq: tr!(search, replace) * * translate the character from search to replace */ static VALUE mm_tr_bang(obj, a, b) VALUE obj, a, b; { VALUE tmp[2]; tmp[0] = a; tmp[1] = b; return mm_bang_i(obj, MM_MODIFY | MM_PROTECT, rb_intern("tr!"), 2, tmp); } /* * call-seq: tr_s!(search, replace) * * translate the character from search to replace, then * squeeze sequence of the same characters */ static VALUE mm_tr_s_bang(obj, a, b) VALUE obj, a, b; { VALUE tmp[2]; tmp[0] = a; tmp[1] = b; return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("tr_s!"), 2, tmp); } /* * call-seq: crypt * * crypt with salt */ static VALUE mm_crypt(a, b) VALUE a, b; { return mm_bang_i(a, MM_ORIGIN, rb_intern("crypt"), 1, &b); } /* * call-seq: include?(other) * * return true if other is found */ static VALUE mm_include(a, b) VALUE a, b; { return mm_bang_i(a, MM_ORIGIN, rb_intern("include?"), 1, &b); } /* * call-seq: index * * return the index of substr */ static VALUE mm_index(int argc, VALUE * argv, VALUE obj) { return mm_bang_i(obj, MM_ORIGIN, rb_intern("index"), argc, argv); } /* * call-seq: rindex(sibstr, pos = nil) * * return the index of the last occurrence of substr */ static VALUE mm_rindex(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_ORIGIN, rb_intern("rindex"), argc, argv); } /* * Document-method: [] * Document-method: slice * * call-seq: [](args) * * Element reference - with the following syntax: * * self[nth] * * retrieve the nth character * * self[start..last] * * return a substring from start to last * * self[start, length] * * return a substring of lenght characters from start */ static VALUE mm_aref_m(int argc, VALUE *argv, VALUE obj) { return mm_bang_i(obj, MM_ORIGIN, rb_intern("[]"), argc, argv); } /* * call-seq: sum(bits = 16) * * return a checksum */ static VALUE mm_sum(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_ORIGIN, rb_intern("sum"), argc, argv); } /* * call-seq: split(sep, limit = 0) * * splits into a list of strings and return this array */ static VALUE mm_split(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_ORIGIN, rb_intern("split"), argc, argv); } /* * call-seq: count(o1, *args) * * each parameter defines a set of character to count */ static VALUE mm_count(argc, argv, obj) int argc; VALUE *argv, obj; { return mm_bang_i(obj, MM_ORIGIN, rb_intern("count"), argc, argv); } static VALUE mm_internal_each(tmp) VALUE *tmp; { return rb_funcall2(tmp[0], (ID)tmp[1], (int)tmp[2], (VALUE *)tmp[3]); } /* * call-seq: scan(pattern, &block) * * return an array of all occurence matched by pattern */ static VALUE mm_scan(obj, a) VALUE obj, a; { VALUE tmp[4]; if (!rb_block_given_p()) { return rb_funcall(mm_str(obj, MM_ORIGIN), rb_intern("scan"), 1, a); } tmp[0] = mm_str(obj, MM_ORIGIN); tmp[1] = (VALUE)rb_intern("scan"); tmp[2] = (VALUE)1; tmp[3] = (VALUE)&a; rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); return obj; } /* * Document-method: each * Document-method: each_line * * call-seq: * each(rs = $/, &block) * * iterate on each line */ static VALUE mm_each_line(argc, argv, obj) int argc; VALUE obj, *argv; { VALUE tmp[4]; tmp[0] = mm_str(obj, MM_ORIGIN); tmp[1] = (VALUE)rb_intern("each_line"); tmp[2] = (VALUE)argc; tmp[3] = (VALUE)argv; rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); return obj; } /* * call-seq: each_byte(&block) * * iterate on each byte */ static VALUE mm_each_byte(argc, argv, obj) int argc; VALUE obj, *argv; { VALUE tmp[4]; tmp[0] = mm_str(obj, MM_ORIGIN); tmp[1] = (VALUE)rb_intern("each_byte"); tmp[2] = (VALUE)argc; tmp[3] = (VALUE)argv; rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); return obj; } /* * Document-method: lockall * Document-method: mlockall * * call-seq: * lockall(flag) * * disable paging of all pages mapped. flag can be * Mmap::MCL_CURRENT or Mmap::MCL_FUTURE */ static VALUE mm_mlockall(obj, flag) VALUE obj, flag; { if (mlockall(NUM2INT(flag)) == -1) { rb_raise(rb_eArgError, "mlockall(%d)", errno); } return Qnil; } /* * Document-method: unlockall * Document-method: munlockall * * call-seq: unlockall * * reenable paging */ static VALUE mm_munlockall(obj) VALUE obj; { if (munlockall() == -1) { rb_raise(rb_eArgError, "munlockall(%d)", errno); } return Qnil; } /* * Document-method: lock * Document-method: mlock * * call-seq: mlock * * disable paging */ static VALUE mm_mlock(obj) VALUE obj; { mm_ipc *i_mm; Data_Get_Struct(obj, mm_ipc, i_mm); if (i_mm->t->flag & MM_LOCK) { return obj; } if (i_mm->t->flag & MM_ANON) { rb_raise(rb_eArgError, "mlock(anonymous)"); } if (mlock(i_mm->t->addr, i_mm->t->len) == -1) { rb_raise(rb_eArgError, "mlock(%d)", errno); } i_mm->t->flag |= MM_LOCK; return obj; } /* * Document-method: munlock * Document-method: unlock * * call-seq: unlock * * reenable paging */ static VALUE mm_munlock(obj) VALUE obj; { mm_ipc *i_mm; Data_Get_Struct(obj, mm_ipc, i_mm); if (!(i_mm->t->flag & MM_LOCK)) { return obj; } if (munlock(i_mm->t->addr, i_mm->t->len) == -1) { rb_raise(rb_eArgError, "munlock(%d)", errno); } i_mm->t->flag &= ~MM_LOCK; return obj; } void Init_mmap() { if (rb_const_defined_at(rb_cObject, rb_intern("Mmap"))) { rb_raise(rb_eNameError, "class already defined"); } mm_cMap = rb_define_class("Mmap", rb_cObject); rb_define_const(mm_cMap, "MS_SYNC", INT2FIX(MS_SYNC)); rb_define_const(mm_cMap, "MS_ASYNC", INT2FIX(MS_ASYNC)); rb_define_const(mm_cMap, "MS_INVALIDATE", INT2FIX(MS_INVALIDATE)); rb_define_const(mm_cMap, "PROT_READ", INT2FIX(PROT_READ)); rb_define_const(mm_cMap, "PROT_WRITE", INT2FIX(PROT_WRITE)); rb_define_const(mm_cMap, "PROT_EXEC", INT2FIX(PROT_EXEC)); rb_define_const(mm_cMap, "PROT_NONE", INT2FIX(PROT_NONE)); rb_define_const(mm_cMap, "MAP_SHARED", INT2FIX(MAP_SHARED)); rb_define_const(mm_cMap, "MAP_PRIVATE", INT2FIX(MAP_PRIVATE)); #ifdef MADV_NORMAL rb_define_const(mm_cMap, "MADV_NORMAL", INT2FIX(MADV_NORMAL)); rb_define_const(mm_cMap, "MADV_RANDOM", INT2FIX(MADV_RANDOM)); rb_define_const(mm_cMap, "MADV_SEQUENTIAL", INT2FIX(MADV_SEQUENTIAL)); rb_define_const(mm_cMap, "MADV_WILLNEED", INT2FIX(MADV_WILLNEED)); rb_define_const(mm_cMap, "MADV_DONTNEED", INT2FIX(MADV_DONTNEED)); #endif #ifdef MAP_DENYWRITE rb_define_const(mm_cMap, "MAP_DENYWRITE", INT2FIX(MAP_DENYWRITE)); #endif #ifdef MAP_EXECUTABLE rb_define_const(mm_cMap, "MAP_EXECUTABLE", INT2FIX(MAP_EXECUTABLE)); #endif #ifdef MAP_NORESERVE rb_define_const(mm_cMap, "MAP_NORESERVE", INT2FIX(MAP_NORESERVE)); #endif #ifdef MAP_LOCKED rb_define_const(mm_cMap, "MAP_LOCKED", INT2FIX(MAP_LOCKED)); #endif #ifdef MAP_GROWSDOWN rb_define_const(mm_cMap, "MAP_GROWSDOWN", INT2FIX(MAP_GROWSDOWN)); #endif #ifdef MAP_ANON rb_define_const(mm_cMap, "MAP_ANON", INT2FIX(MAP_ANON)); #endif #ifdef MAP_ANONYMOUS rb_define_const(mm_cMap, "MAP_ANONYMOUS", INT2FIX(MAP_ANONYMOUS)); #endif #ifdef MAP_NOSYNC rb_define_const(mm_cMap, "MAP_NOSYNC", INT2FIX(MAP_NOSYNC)); #endif #ifdef MCL_CURRENT rb_define_const(mm_cMap, "MCL_CURRENT", INT2FIX(MCL_CURRENT)); rb_define_const(mm_cMap, "MCL_FUTURE", INT2FIX(MCL_FUTURE)); #endif rb_define_alloc_func(mm_cMap, mm_s_alloc); rb_define_singleton_method(mm_cMap, "new", mm_s_new, -1); rb_define_singleton_method(mm_cMap, "mlockall", mm_mlockall, 1); rb_define_singleton_method(mm_cMap, "lockall", mm_mlockall, 1); rb_define_singleton_method(mm_cMap, "munlockall", mm_munlockall, 0); rb_define_singleton_method(mm_cMap, "unlockall", mm_munlockall, 0); rb_define_method(mm_cMap, "initialize", mm_init, -1); rb_define_method(mm_cMap, "unmap", mm_unmap, 0); rb_define_method(mm_cMap, "munmap", mm_unmap, 0); rb_define_method(mm_cMap, "msync", mm_msync, -1); rb_define_method(mm_cMap, "sync", mm_msync, -1); rb_define_method(mm_cMap, "flush", mm_msync, -1); rb_define_method(mm_cMap, "mprotect", mm_mprotect, 1); rb_define_method(mm_cMap, "protect", mm_mprotect, 1); #ifdef MADV_NORMAL rb_define_method(mm_cMap, "madvise", mm_madvise, 1); rb_define_method(mm_cMap, "advise", mm_madvise, 1); #endif rb_define_method(mm_cMap, "mlock", mm_mlock, 0); rb_define_method(mm_cMap, "lock", mm_mlock, 0); rb_define_method(mm_cMap, "munlock", mm_munlock, 0); rb_define_method(mm_cMap, "unlock", mm_munlock, 0); rb_define_method(mm_cMap, "extend", mm_extend, 1); rb_define_method(mm_cMap, "freeze", mm_freeze, 0); rb_define_method(mm_cMap, "<=>", mm_cmp, 1); rb_define_method(mm_cMap, "==", mm_equal, 1); rb_define_method(mm_cMap, "===", mm_equal, 1); rb_define_method(mm_cMap, "eql?", mm_eql, 1); rb_define_method(mm_cMap, "hash", mm_hash, 0); #if HAVE_RB_STR_CASECMP rb_define_method(mm_cMap, "casecmp", mm_casecmp, 1); #endif rb_define_method(mm_cMap, "[]", mm_aref_m, -1); rb_define_method(mm_cMap, "[]=", mm_aset_m, -1); #if HAVE_RB_STR_INSERT rb_define_method(mm_cMap, "insert", mm_insert, 2); #endif rb_define_method(mm_cMap, "length", mm_size, 0); rb_define_method(mm_cMap, "size", mm_size, 0); rb_define_method(mm_cMap, "empty?", mm_empty, 0); rb_define_method(mm_cMap, "=~", mm_match, 1); #if HAVE_RB_STR_MATCH rb_define_method(mm_cMap, "match", mm_match_m, 1); #endif rb_define_method(mm_cMap, "index", mm_index, -1); rb_define_method(mm_cMap, "rindex", mm_rindex, -1); rb_define_method(mm_cMap, "to_str", mm_to_str, 0); rb_define_method(mm_cMap, "upcase!", mm_upcase_bang, 0); rb_define_method(mm_cMap, "downcase!", mm_downcase_bang, 0); rb_define_method(mm_cMap, "capitalize!", mm_capitalize_bang, 0); rb_define_method(mm_cMap, "swapcase!", mm_swapcase_bang, 0); rb_define_method(mm_cMap, "split", mm_split, -1); rb_define_method(mm_cMap, "reverse!", mm_reverse_bang, 0); rb_define_method(mm_cMap, "concat", mm_concat, 1); rb_define_method(mm_cMap, "<<", mm_concat, 1); rb_define_method(mm_cMap, "crypt", mm_crypt, 1); rb_define_method(mm_cMap, "include?", mm_include, 1); rb_define_method(mm_cMap, "scan", mm_scan, 1); rb_define_method(mm_cMap, "sub!", mm_sub_bang, -1); // TODO: fix mm_gsub_bang_int // rb_define_method(mm_cMap, "gsub!", mm_gsub_bang, -1); rb_define_method(mm_cMap, "strip!", mm_strip_bang, 0); #if HAVE_RB_STR_LSTRIP rb_define_method(mm_cMap, "lstrip!", mm_lstrip_bang, 0); rb_define_method(mm_cMap, "rstrip!", mm_rstrip_bang, 0); #endif rb_define_method(mm_cMap, "chop!", mm_chop_bang, 0); rb_define_method(mm_cMap, "chomp!", mm_chomp_bang, -1); rb_define_method(mm_cMap, "count", mm_count, -1); rb_define_method(mm_cMap, "tr!", mm_tr_bang, 2); rb_define_method(mm_cMap, "tr_s!", mm_tr_s_bang, 2); rb_define_method(mm_cMap, "delete!", mm_delete_bang, -1); rb_define_method(mm_cMap, "squeeze!", mm_squeeze_bang, -1); rb_define_method(mm_cMap, "each_line", mm_each_line, -1); rb_define_method(mm_cMap, "each", mm_each_line, -1); rb_define_method(mm_cMap, "each_byte", mm_each_byte, -1); rb_define_method(mm_cMap, "sum", mm_sum, -1); rb_define_method(mm_cMap, "slice", mm_aref_m, -1); rb_define_method(mm_cMap, "slice!", mm_slice_bang, -1); rb_define_method(mm_cMap, "semlock", mm_semlock, -1); rb_define_method(mm_cMap, "ipc_key", mm_ipc_key, 0); } mmap2-2.2.7/b.rb0000644000175000017500000000140213155520113012321 0ustar pravipravi#!/usr/bin/ruby $LOAD_PATH.unshift "." require "mmap" PAGESIZE = 4096 f = File.open("aa", "w") f.write("\0" * PAGESIZE) f.write("b.rb") f.write("\0" * PAGESIZE) f.close m = Mmap.new("aa", "w", "offset" => 0) p m.size == "b.rb".size + 2 * PAGESIZE p m.scan(/[a-z.]+/) == ["b.rb"] p m.index("b.rb") == PAGESIZE p m.rindex("b.rb") == PAGESIZE p m.sub!(/[a-z.]+/, "toto") == m p m.scan(/[a-z.]+/) == ["toto"] begin m.sub!(/[a-z.]+/, "alpha") puts "not OK must give an error" rescue puts "OK : #$!" end m.munmap m = Mmap.new("aa", "rw") p m.index("toto") == PAGESIZE p m.sub!(/([a-z.]+)/, "alpha") == m p $& == "toto" p $1 == "toto" p m.index("toto") == nil p m.index("alpha") == PAGESIZE p m.size == 5 + 2 * PAGESIZE m.gsub!(/\0/, "X") p m.size == 5 + 2 * PAGESIZE mmap2-2.2.7/README.rdoc0000644000175000017500000000100313155520113013356 0ustar pravipravi= Mmap * http://rubyforge.org/frs/?group_id=8350 == DESCRIPTION The Mmap class implement memory-mapped file objects == SYNOPSIS require 'mmap' mmap = Mmap.new(__FILE__) mmap.advise(Mmap::MADV_SEQUENTIAL) mmap.each do |line| puts line end == Installation gem install mmap == Documentation rake docs == Copying This extension module is copyrighted free software by Guy Decoux You can redistribute it and/or modify it under the same term as Ruby. Guy Decoux mmap2-2.2.7/test/0000755000175000017500000000000013155520113012535 5ustar pravipravimmap2-2.2.7/test/test_mmap.rb0000644000175000017500000002734013155520113015061 0ustar pravipravirequire 'mmap' require 'fileutils' require 'tempfile' require 'minitest/autorun' class TestMmap < Minitest::Test EXT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'ext', 'mmap')) def setup @tmp = Dir.tmpdir FileUtils.cp(File.join(EXT_DIR, 'mmap.c'), @tmp) @mmap_c = File.join(@tmp, 'mmap.c') @mmap = Mmap.new(@mmap_c, "rw") @str = File.read @mmap_c end def teardown @mmap.unmap aa = File.join(@tmp, 'aa') bb = File.join(@tmp, 'bb') FileUtils.rm(aa) if File.exist?(aa) FileUtils.rm(bb) if File.exist?(bb) end def internal_read File.readlines(@mmap_c, nil)[0] end def test_inspect assert @mmap.inspect end # Make sure clone raises. Cloning would be bad. def test_clone assert_raises(TypeError) do @mmap.clone end end # Make sure dup raises. Cloning would be bad. def test_dup assert_raises(TypeError) do @mmap.dup end end def test_length assert_equal(@mmap.length, @str.length, "") end def test_simple_aref assert_equal(@str[10], @mmap[10], ""); end def test_aref max = @str.size * 2 72.times do ran1 = rand(max) assert_equal(@str[ran1], @mmap[ran1], ""); assert_equal(@str[-ran1], @mmap[-ran1], ""); ran2 = rand(max) assert_equal(@str[ran1, ran2], @mmap[ran1, ran2], ""); assert_equal(@str[-ran1, ran2], @mmap[-ran1, ran2], ""); assert_equal(@str[ran1, -ran2], @mmap[ran1, -ran2], ""); assert_equal(@str[-ran1, -ran2], @mmap[-ran1, -ran2], ""); assert_equal(@str[ran1 .. ran2], @mmap[ran1 .. ran2], ""); assert_equal(@str[-ran1 .. ran2], @mmap[-ran1 .. ran2], ""); assert_equal(@str[ran1 .. -ran2], @mmap[ran1 .. -ran2], ""); assert_equal(@str[-ran1 .. -ran2], @mmap[-ran1 .. -ran2], ""); end assert_equal(@str[/random/], @mmap[/random/], "") assert_equal(@str[/real/], @mmap[/real/], "") assert_equal(@str[/none/], @mmap[/none/], "") end def internal_aset(a, b = nil, c = true) access = if b repl = '' rand(12).times do repl << (65 + rand(25)) end if c "[a, b] = '#{repl}'" else "[a .. b] = '#{repl}'" end else "[a] = #{(65 + rand(25))}.chr" end begin eval "@str#{access}" rescue IndexError, RangeError begin eval "@mmap#{access}" rescue IndexError, RangeError else flunk("*must* fail with IndexError") end else eval "@mmap#{access}" end assert_equal(@mmap.to_str, @str, "") end def test_aset @mmap[/...../] = "change it" @str[/...../] = "change it" assert_equal(@mmap.to_str, @str, "aset regexp") @mmap["ge i"] = "change it" @str["ge i"] = "change it" assert_equal(@mmap.to_str, @str, "aset regexp") max = @str.size * 2 72.times do ran1 = rand(max) internal_aset(ran1) internal_aset(-ran1) ran2 = rand(max) internal_aset(ran1, ran2) internal_aset(ran1, -ran2) internal_aset(-ran1, ran2) internal_aset(-ran1, -ran2) internal_aset(ran1, ran2, false) internal_aset(ran1, -ran2, false) internal_aset(-ran1, ran2, false) internal_aset(-ran1, -ran2, false) end end def internal_slice(a, b = nil, c = true) access = if b if c ".slice!(a, b)" else ".slice!(a .. b)" end else ".slice!(a)" end begin eval "@str#{access}" rescue IndexError, RangeError begin eval "@mmap#{access}" rescue IndexError, RangeError else flunk("*must* fail with IndexError") end else eval "@mmap#{access}" end assert_equal(@mmap.to_str, @str, "") end def test_slice max = @str.size * 2 72.times do ran1 = rand(max) internal_slice(ran1) internal_slice(-ran1) ran2 = rand(max) internal_slice(ran1, ran2) internal_slice(ran1, -ran2) internal_slice(-ran1, ran2) internal_slice(-ran1, -ran2) internal_slice(ran1, ran2, false) internal_slice(ran1, -ran2, false) internal_slice(-ran1, ran2, false) internal_slice(-ran1, -ran2, false) end end def test_easy_sub! assert_equal(@mmap.index("rb_raise"), @mmap.index("rb_raise"), "") end def test_reg assert_equal(@str.scan(/include/), @mmap.scan(/include/), "") assert_equal(@mmap.index("rb_raise"), @mmap.index("rb_raise"), "") assert_equal(@mmap.rindex("rb_raise"), @mmap.rindex("rb_raise"), "") assert_equal(@mmap.index(/rb_raise/), @mmap.index(/rb_raise/), "") assert_equal(@mmap.rindex(/rb_raise/), @mmap.rindex(/rb_raise/), "") ('a' .. 'z').each do |i| assert_equal(@mmap.index(i), @str.index(i), "") assert_equal(@mmap.rindex(i), @str.rindex(i), "") assert_equal(@mmap.index(i), @str.index(/#{i}/), "") assert_equal(@mmap.rindex(i), @str.rindex(/#{i}/), "") end @mmap.sub!(/GetMmap/, 'XXXX'); @str.sub!(/GetMmap/, 'XXXX') assert_equal(@str, @mmap.to_str, "") @mmap.gsub!(/GetMmap/, 'XXXX'); @str.gsub!(/GetMmap/, 'XXXX') assert_equal(@mmap.to_str, @str, "") @mmap.gsub!(/YYYY/, 'XXXX'); @str.gsub!(/YYYY/, 'XXXX') assert_equal(@mmap.to_str, @str, "") assert_equal(@mmap.split(/\w+/), @str.split(/\w+/), "") assert_equal(@mmap.split(/\W+/), @str.split(/\W+/), "") assert_equal(@mmap.crypt("abc"), @str.crypt("abc"), "") end def internal_modify idmod, *args if res = @str.method(idmod)[*args] assert_equal(@mmap.method(idmod)[*args].to_str, res, "<#{idmod}>") else assert_equal(@mmap.method(idmod)[*args], res, "<#{idmod}>") end end def test_modify skip "pending" internal_modify(:reverse!) internal_modify(:upcase!) internal_modify(:downcase!) internal_modify(:capitalize!) internal_modify(:swapcase!) internal_modify(:strip!) internal_modify(:chop!) internal_modify(:chomp!) internal_modify(:squeeze!) internal_modify(:tr!, 'abcdefghi', '123456789') internal_modify(:tr_s!, 'jklmnopqr', '123456789') internal_modify(:delete!, 'A-Z') end def test_iterate mmap = []; @mmap.each_byte {|l| mmap << l} str = []; @str.each_byte {|l| str << l} assert_equal(mmap, str, "") end def test_concat [@mmap, @str].each {|l| l << "bc"; l << 12; l << "ab"} assert_equal(@mmap.to_str, @str, "<<") assert_raises(TypeError) { @mmap << 456 } end def test_extend @mmap.extend(4096) assert_equal(@mmap.to_str, @str, "extend") if @str.respond_to?(:insert) 10.times do pos = rand(@mmap.size) str = "XX" * rand(66) @str.insert(pos, str) @mmap.insert(pos, str) assert_equal(@mmap.to_str, @str, "insert") end end end def test_msync 3.times do |i| [@mmap, @str].each {|l| l << "x" * 4096 } str = internal_read if str != @mmap.to_str @mmap.msync assert_equal(@mmap.to_str, internal_read, "msync") break end end end def test_protect assert_equal(@mmap, @mmap.protect("w"), "protect") assert_equal("a", @mmap[12] = "a", "affect") @str[12] = "a" assert_equal(@mmap.to_str, @str, "protect") assert_raises(TypeError) { @mmap << "a" } assert_equal(@mmap, @mmap.protect("r"), "protect") assert_raises(RuntimeError) { @mmap[12] = "a" } assert_raises(RuntimeError) { @mmap.protect("rw") } end def test_anonymous if defined?(Mmap::MAP_ANONYMOUS) assert_kind_of(Mmap, @mmap = Mmap.new(nil, "length" => 8192, "offset" => 12, "increment" => 1024, "initialize" => " ")) @str = " " * 8192 1024.times do pos = rand(8192) @mmap[pos] = @str[pos] = (32 + rand(64)).chr end assert_equal(@mmap.to_str, @str, "insert anonymous") assert_raises(IndexError) { @mmap[12345] = "a" } assert_raises(TypeError) { @mmap << "a" } end end def test_fileno @mmap = Mmap.new(File.new(@mmap_c, "r+"), "rw") test_aref @mmap[12] = "3"; @str[12] = "3" assert_equal(@mmap.to_str, @str, "insert io") assert_equal(0, @mmap <=> @str, "cmp") assert_raises(TypeError) { @mmap[12] = "ab" } @mmap.freeze if @str.respond_to?(:match) assert_equal(@str.match("rb_match_busy").offset(0), @mmap.match("rb_match_busy").offset(0), "match") assert_equal(@str.match(/rb_../).offset(0), @mmap.match(/rb_../).offset(0), "match") assert_equal(@str.match("rb_match_buzy"), @mmap.match("rb_match_buzy"), "no match") assert_equal(@str =~ /rb_match_busy/, @mmap =~ /rb_match_busy/, "match") assert_equal(@str =~ /rb_match_buzy/, @mmap =~ /rb_match_buzy/, "no match") end assert_raises(RuntimeError) { @mmap[12] = "a" } end def test_div string = "azertyuiopqsdfghjklm" assert_kind_of(Mmap, m0 = Mmap.new("#{@tmp}/aa", "a"), "new a") File.open("#{@tmp}/bb", "w") {|f| f.puts "aaa" } assert_kind_of(Mmap, m1 = Mmap.new("#{@tmp}/bb", "w"), "new a") assert_equal(true, m0.empty?, "empty") assert_equal(true, m1.empty?, "empty") assert_equal(m0, m0 << string, "<<") assert_equal(m1, m1 << string, "<<") assert_equal(false, m0.empty?, "empty") assert_equal(false, m1.empty?, "empty") assert_equal(true, m0 == m1, "==") if string.respond_to?(:casecmp) assert_equal(0, m0.casecmp(string.upcase), "casecmp") assert_equal(0, m0.casecmp(m1), "casecmp") end assert_equal(true, m0 === m1, "===") assert_equal(false, m0 === string, "===") assert_equal(true, m0.eql?(m1), ".eql?") assert_equal(true, m1.eql?(m0), ".eql?") assert_equal(false, m1.eql?(string), ".eql?") assert_equal(m0.hash, m1.hash, "hash") assert_equal(true, m0.include?("azert"), "include") assert_equal(false, m1.include?("aqert"), "include") i = 0 m0.scan(/./) {|c| assert_equal(c, string[i,1], "scan"); i += 1} assert_nil(m0.munmap, "munmap") assert_nil(m1.munmap, "munmap") end def test_other test_div if File.exist?("#{@tmp}/aa") string = "azertyuiopqsdfghjklm" assert_kind_of(Mmap, m0 = Mmap.new("#{@tmp}/aa", "r"), "new r") assert_equal(string, m0.to_str, "content") assert_raises(RuntimeError) { m0[0] = 12 } assert_raises(RuntimeError) { m0 << 12 } assert_nil(m0.munmap, "munmap") if defined?(Mmap::MAP_ANONYMOUS) assert_raises(ArgumentError) { Mmap.new(nil, "w") } assert_kind_of(Mmap, m0 = Mmap.new(nil, 12), "new w") assert_equal(false, m0.empty?, "empty") assert_equal("a", m0[0] = "a", "set") assert_raises(TypeError) { m0 << 12 } if defined?(Mmap::MADV_DONTNEED) assert_nil(m0.advise(Mmap::MADV_DONTNEED), "advise") assert_equal("a", m0[0,1], "get") end assert_equal(m0, m0.sub!(/./) { "y" }, "sub") assert_equal(m0, m0.gsub!(/./) { "x" }, "gsub") assert_equal("x" * 12, m0.to_str, "retrieve") assert_equal("ab", m0[1..2] = "ab", "range") assert_raises(TypeError) { m0[1..2] = "abc" } assert_raises(ArgumentError) { m0.lock } assert_raises(ArgumentError) { Mmap::lockall(0) } assert_nil(m0.munmap, "munmap") end end end end