pmahoney/process_shared

View on GitHub
lib/process_shared/shared_memory_io.rb

Summary

Maintainability
C
1 day
Test Coverage
module ProcessShared
  # Does some bounds checking for EOF, but assumes underlying memory
  # object (FFI::Pointer) will do bounds checking, in particular the
  # {#_putbytes} method relies on this.
  #
  # Note: an unbounded FFI::Pointer may be converted into a bounded
  # pointer using +ptr.slice(0, size)+.
  class SharedMemoryIO

    attr_accessor :pos
    attr_reader :mem

    def initialize(mem)
      @mem = mem
      @pos = 0
      @closed = false           # TODO: actually pay attention to this
    end
    
    def <<(*args)
      raise NotImplementedError
    end

    def advise(*args)
      # no-op
    end
    
    def autoclose=(*args)
      raise NotImplementedError
    end

    def autoclose?
      raise NotImplementedError
    end

    def binmode
      # no-op; always in binmode
    end

    def binmode?
      true
    end

    def bytes
      if block_given?
        until eof?
          yield _getbyte
        end
      else
        raise NotImplementedError
      end
    end
    alias_method :each_byte, :bytes

    def chars
      raise NotImplementedError
    end
    alias_method :each_char, :chars

    def close
      @closed = true
    end

    def close_on_exec=(bool)
      raise NotImplementedError
    end

    def close_on_exec?
      raise NotImplementedError
    end

    def close_read
      raise NotImplementedError
    end

    def close_write
      raise NotImplementedError
    end

    def closed?
      @closed
    end

    def codepoints
      raise NotImplementedError
    end
    alias_method :each_codepoint, :codepoints

    def each
      raise NotImplementedError
    end
    alias_method :each_line, :each
    alias_method :lines, :each

    def eof?
      pos == mem.size
    end
    alias_method :eof, :eof?

    def external_encoding
      raise NotImplementedError
    end

    def fcntl
      raise NotImplementedError
    end

    def fdatasync
      raise NotImplementedError
    end

    def fileno
      raise NotImplementedError
    end
    alias_method :to_i, :fileno

    def flush
      # no-op
    end

    def fsync
      raise NotImplementedError
    end

    def getbyte
      return nil if eof?
      _getbyte
    end

    # {#getc} in Ruby 1.9 returns String or nil.  In 1.8, it returned
    # Fixnum of nil (identical to getbyte).
    #
    # FIXME: should this be encoding/character aware?
    def getc19
      if b = getbyte
        '' << b
      end
    end
    # FIXME: ignores versions prior to 1.8.
    if RUBY_VERSION =~ /^1.8/
      alias_method :getc, :getbyte
    else
      alias_method :getc, :getc19
    end

    def gets
      raise NotImplementedError
    end

    def internal_encoding
      raise NotImplementedError
    end

    def ioctl
      raise NotImplementedError
    end

    def tty?
      false
    end
    alias_method :isatty, :tty?

    def lineno
      raise NotImplementedError
    end

    def lineno=
      raise NotImplementedError
    end

    def lines
      raise NotImplementedError
    end

    def pid
      raise NotImplementedError
    end

    alias_method :tell, :pos

    def print(*args)
      raise NotImplementedError
    end
    def printf(*args)
      raise NotImplementedError
    end

    def putc(arg)
      raise NotImplementedError
    end

    def puts(*args)
      raise NotImplementedError
    end

    # FIXME: this doesn't match IO#read exactly (corner cases about
    # EOF and whether length was nil or not), but it's enough for
    # {Marshal::load}.
    def read(length = nil, buffer = nil)
      length ||= (mem.size - pos)
      buffer ||= ''
      buffer.force_encoding('ASCII-8BIT') unless RUBY_VERSION.start_with?('1.8')
      
      actual_length = [(mem.size - pos), length].min
      actual_length.times do
        buffer << _getbyte
      end
      buffer
    end

    def read_nonblock(*args)
      raise NotImplementedError
    end

    def readbyte
      raise EOFError if eof?
      _getbyte
    end

    def readchar
      raise NotImplementedError
    end

    def readline
      raise NotImplementedError
    end

    def readlines
      raise NotImplementedError
    end

    def readpartial
      raise NotImplementedError
    end

    def reopen
      raise NotImplementedError
    end

    def rewind
      pos = 0
    end

    def seek(amount, whence = IO::SEEK_SET)
      case whence
      when IO::SEEK_CUR
        self.pos += amount
      when IO::SEEK_END
        self.pos = (mem.size + amount)
      when IO::SEEK_SET
        self.pos = amount
      else
        raise ArgumentError, "bad seek whence #{whence}"
      end
    end

    def set_encoding
      raise NotImplementedError
    end

    def stat
      raise NotImplementedError
    end

    def sync
      true
    end

    def sync=
      raise NotImplementedError
    end

    def sysread(*args)
      raise NotImplementedError
    end

    def sysseek(*args)
      raise NotImplementedError
    end

    def syswrite(*args)
      raise NotImplementedError
    end

    def to_io
      raise NotImplementedError
    end

    def ungetbyte
      raise IOError if pos == 0
      pos -= 1
    end

    def ungetc
      raise NotImplementedError
    end

    def write(str)
      s = str.to_s
      _putbytes(s)
      s.size
    end

    def write_nonblock(str)
      raise NotImplementedError
    end

    private

    # Like {#getbyte} but does not perform eof check.
    def _getbyte
      b = mem.get_uchar(pos)
      self.pos += 1
      b
    end

    def _putbytes(str)
      mem.put_bytes(pos, str, 0, str.size)
      self.pos += str.size
    end

  end
end