class Metasm::VirtualString

This class implements an objects that behaves like a regular string, but whose real data is dynamically fetched or generated on demand its size is immutable implements a page cache substrings are Strings (small substring) or another VirtualString (a kind of 'window' on the original VString, when the substring length is > 4096)

Attributes

addr_start[RW]

the real address of our first byte

length[RW]

our length

pagecache[RW]

array of [addr, raw data], sorted by first == last accessed

pagecache_len[RW]

maximum length of self.pagecache (number of cached pages)

Public Class Methods

new(addr_start, length) click to toggle source
# File metasm/os/main.rb, line 198
def initialize(addr_start, length)
        @addr_start = addr_start
        @length = length
        @pagecache = []
        @pagecache_len = 4
        @pagelength ||= 4096  # must be (1 << x)
end

Public Instance Methods

=~(o) click to toggle source

'=~' does not go through #method_missing

# File metasm/os/main.rb, line 184
def =~(o)
        realstring =~ o
end
[](from, len=nil) click to toggle source

formats parameters for reading

# File metasm/os/main.rb, line 79
def [](from, len=nil)
        if not len and from.kind_of? Range
                b = from.begin
                e = from.end
                b = b + length if b < 0
                e = e + length if e < 0
                len = e - b
                len += 1 if not from.exclude_end?
                from = b
        end
        from = from + length if from < 0

        return nil if from > length or (from == length and not len)
        len = length - from if len and from + len > length
        return '' if len == 0

        read_range(from, len)
end
[]=(from, len, val=nil) click to toggle source

formats parameters for overwriting portion of the string

# File metasm/os/main.rb, line 99
def []=(from, len, val=nil)
        raise TypeError, 'cannot modify frozen virtualstring' if frozen?

        if not val
                val = len
                len = nil
        end
        if not len and from.kind_of? Range
                b = from.begin
                e = from.end
                b = b + length if b < 0
                e = e + length if e < 0
                len = e - b
                len += 1 if not from.exclude_end?
                from = b
        elsif not len
                len = 1
                val = val.chr
        end
        from = from + length if from < 0

        raise IndexError, 'Index out of string' if from > length
        raise IndexError, 'Cannot modify virtualstring length' if val.length != len or from + len > length

        write_range(from, val)
end
cache_get_page(addr) click to toggle source

searches the cache for a page containing addr, updates if not found

# File metasm/os/main.rb, line 224
def cache_get_page(addr)
        addr &= ~(@pagelength-1)
        i = 0
        @pagecache.each { |c|
                if addr == c[0]
                        # most recently used first
                        @pagecache.unshift @pagecache.delete_at(i) if i != 0
                        return c
                end
                i += 1
        }
        @pagecache.pop if @pagecache.length >= @pagecache_len
        c = [addr]
        p = get_page(addr)
        c << p.to_s.ljust(@pagelength, "\0")
        c << true if not p
        @pagecache.unshift c
        c
end
empty?() click to toggle source

avoid triggering realstring from #method_missing if possible

# File metasm/os/main.rb, line 157
def empty?
        length == 0
end
index(chr, base=0) click to toggle source

avoid triggering realstring from #method_missing if possible heavily used in to find 0-terminated strings in ExeFormats

# File metasm/os/main.rb, line 163
def index(chr, base=0)
        return if base >= length or base <= -length
        if i = self[base, 64].index(chr) or i = self[base, @pagelength].index(chr)
                base + i
        else
                realstring.index(chr, base)
        end
end
invalidate() click to toggle source

invalidates the page cache

# File metasm/os/main.rb, line 212
def invalidate
        @pagecache.clear
end
method_missing(m, *args, &b) click to toggle source

forwards unhandled messages to a frozen realstring

Calls superclass method
# File metasm/os/main.rb, line 147
def method_missing(m, *args, &b)
        if ''.respond_to? m
                puts "Using VirtualString.realstring for #{m} from:", caller if $DEBUG
                realstring.freeze.send(m, *args, &b)
        else
                super(m, *args, &b)
        end
end
page_invalid?(addr) click to toggle source

returns wether a page is valid or not

# File metasm/os/main.rb, line 207
def page_invalid?(addr)
        cache_get_page(@addr_start+addr)[2]
end
read_range(from, len) click to toggle source

reads a range from the page cache returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes

# File metasm/os/main.rb, line 246
def read_range(from, len)
        from += @addr_start
        if not len
                base, page = cache_get_page(from)
                page[from - base]
        elsif len <= @pagelength
                base, page = cache_get_page(from)
                s = page[from - base, len]
                if from+len-base > @pagelength               # request crosses a page boundary
                        base, page = cache_get_page(from+len)
                        s << page[0, from+len-base]
                end
                s
        else
                # big request: return a new virtual page
                dup(from, len)
        end
end
realstring() click to toggle source

returns the full raw data

# File metasm/os/main.rb, line 127
def realstring
        ret = ''
        addr = 0
        len = length
        while len > @pagelength
                ret << self[addr, @pagelength]
                addr += @pagelength
                len -= @pagelength
        end
        ret << self[addr, len]
end
rindex(chr, max=length) click to toggle source
# File metasm/os/main.rb, line 172
def rindex(chr, max=length)
        return if max > length
        if max > 64 and i = self[max-64, 64].rindex(chr)
                max - 64 + i
        elsif max > @pagelength and i = self[max-@pagelength, @pagelength].rindex(chr)
                max - @pagelength + i
        else
                realstring.rindex(chr, max)
        end
end
to_str() click to toggle source

alias to realstring for bad people checking respond_to? :to_str (like String#<<) XXX alias does not work (not virtual (a la C++))

# File metasm/os/main.rb, line 142
def to_str
        realstring
end
write_range(from, content) click to toggle source

rewrites a segment of data the length written is the length of the content (a VirtualString cannot grow/shrink)

# File metasm/os/main.rb, line 267
def write_range(from, content)
        invalidate
        rewrite_at(from + @addr_start, content)
end