memory_io 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e17e1f92566207d70c15b219aee13f363351c8e6
4
- data.tar.gz: 6de663775256debf502563107010d6c058a9476c
2
+ SHA256:
3
+ metadata.gz: 0f287dc7b3453557bbcda2f2ce09e03d779d0dbff69f8ef059069f03c76da427
4
+ data.tar.gz: a471ab55f6abd70783a220d0939cdd8f84231f59a533a6470a9f64119c184a41
5
5
  SHA512:
6
- metadata.gz: 0c5cc2882d17b308bcd2edd3bd298dc063555635e232f489c20cf378811272839dcce6fead1dd22605d6b77795acb3e08623e74d1650fadaa0ef62229083e8db
7
- data.tar.gz: 937ba157cbf68e9e207aacefe2b00bcc1153cd128e41ec1fff1be1d00d3715c3ed6258d7e4eb5d3c0cab40fc2d486a0158f23ad73d8b98c12d4fff76408e7c6c
6
+ metadata.gz: 959a45d2dcab5ced28be35f6988a6c6fadbd1fd6510e21747727f16e87cabddf23e951ffba71d8deec4baa1be440ce5bd79b1fe54e1bad66a17deb9185c54db9
7
+ data.tar.gz: a1fe1e032d09a53ce4611e2738255ac1444b5a0283e959606a1da232db28c9d0d42a7277977b17d2bb8fcfa476cc58c53368ad64fa970d0b7b91a5536e59e79e
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 david942j
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- [![Build Status](https://travis-ci.org/david942j/memory_io.svg?branch=master)](https://travis-ci.org/david942j/memory_io)
2
1
  [![Gem Version](https://badge.fury.io/rb/memory_io.svg)](https://badge.fury.io/rb/memory_io)
3
- [![Maintainability](https://api.codeclimate.com/v1/badges/dc8da34c5a8ab0095530/maintainability)](https://codeclimate.com/github/david942j/memory_io/maintainability)
4
- [![Test Coverage](https://api.codeclimate.com/v1/badges/dc8da34c5a8ab0095530/test_coverage)](https://codeclimate.com/github/david942j/memory_io/test_coverage)
5
- [![Inline docs](https://inch-ci.org/github/david942j/memory_io.svg?branch=master)](https://inch-ci.org/github/david942j/memory_io)
2
+ [![Build Status](https://github.com/david942j/memory_io/workflows/build/badge.svg)](https://github.com/david942j/memory_io/actions)
3
+ [![Maintainability](https://qlty.sh/gh/david942j/projects/memory_io/maintainability.svg)](https://qlty.sh/gh/david942j/projects/memory_io)
4
+ [![Code Coverage](https://qlty.sh/gh/david942j/projects/memory_io/coverage.svg)](https://qlty.sh/gh/david942j/projects/memory_io)
5
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/github/david942j/memory_io/)
6
6
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
7
7
 
8
8
  # MemoryIO
@@ -14,27 +14,25 @@ Read/Write complicated structures in memory easily.
14
14
  I usually need to dump a structure, say `string` in C++, from memory for debugging.
15
15
  This is not hard if using gdb.
16
16
  However, gdb doesn't support writing Ruby scripts
17
- (unless you use [gdb-ruby](https://github.com/david942j/gdb-ruby), which has dependency of **MemoryIO**).
18
- So I create this repo and want to make the debug procedure much easier.
17
+ (unless you use [gdb-ruby](https://github.com/david942j/gdb-ruby), which has **MemoryIO** as its dependency).
18
+ So I created this projected to make the debug procedure much easier.
19
19
 
20
- This repo has two main targets:
20
+ This repository has two main goals:
21
21
 
22
- 1. To communiate with memory easily.
22
+ 1. To communicate with memory easily.
23
23
  2. To collect all common structures for debugging/learning.
24
24
 
25
25
  ## Why
26
26
 
27
27
  It's not hard to read/write a process's memory (simply open the file `/proc/$PID/mem`),
28
- but it still worth to wrap it.
28
+ but it's still worthy to make a utility.
29
29
 
30
- This repo also targets to collect all common structures, such as how to parse a C++/Rust/Python object from memory.
30
+ This project also targets to collect all common structures, such as how to parse a C++/Rust/Python object from memory.
31
31
  Therefore, **Pull Requests of adding new structures** are welcome :D
32
32
 
33
33
  ## Supported Platform
34
34
 
35
35
  - Linux
36
- - (TODO) Windows
37
- - (TODO) MacOS
38
36
 
39
37
  ## Implemented Structures
40
38
 
@@ -51,7 +49,7 @@ process.read(0x601000, 1, as: 'basic/u64')
51
49
  process.read(0x601000, 1, as: :u64)
52
50
  ```
53
51
 
54
- Goto [the online document](http://www.rubydoc.info/github/david942j/memory_io/master/MemoryIO/Types) for more details
52
+ Go to [the online document](http://www.rubydoc.info/github/david942j/memory_io/master/MemoryIO/Types) for more details
55
53
  of each type.
56
54
 
57
55
  ### BASIC
@@ -122,7 +120,7 @@ string
122
120
  require 'memory_io'
123
121
  process = MemoryIO.attach(`pidof victim`.to_i)
124
122
 
125
- # An example that read a chunk of pt-malloc.
123
+ # An example that reads a chunk of pt-malloc.
126
124
  read_chunk = lambda do |stream|
127
125
  _prev_size = stream.read(8)
128
126
  size = (stream.read(8).unpack('Q').first & -16) - 8
data/lib/memory_io/io.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'memory_io/types/types'
4
5
 
@@ -11,14 +12,14 @@ module MemoryIO
11
12
  #
12
13
  # @param [#pos, #pos=, #read, #write] stream
13
14
  # The file-like object to be read/written.
14
- # +file+ can be unwritable if you will not invoke any write-related method.
15
+ # +file+ can be un-writable if you will not invoke any write-related method.
15
16
  #
16
17
  # If +stream.read(*)+ returns empty string or +nil+, it would be seen as reaching EOF.
17
18
  def initialize(stream)
18
19
  @stream = stream
19
20
  end
20
21
 
21
- # Read and convert result into custom type/stucture.
22
+ # Read and convert result into custom type/structure.
22
23
  #
23
24
  # @param [Integer] num_elements
24
25
  # Number of elements to be read.
@@ -43,7 +44,7 @@ module MemoryIO
43
44
  #
44
45
  # @return [String, Object, Array<Object>]
45
46
  # There're multiple possible return types,
46
- # which dependes on the value of parameter +num_elements+, +as+, and +force_array+.
47
+ # which depends on the value of parameter +num_elements+, +as+, and +force_array+.
47
48
  #
48
49
  # See examples for clear usage. The rule of return type is listed as following:
49
50
  #
@@ -102,6 +103,7 @@ module MemoryIO
102
103
  def read(num_elements, from: nil, as: nil, force_array: false)
103
104
  stream.pos = from if from
104
105
  return stream.read(num_elements) if as.nil?
106
+
105
107
  conv = to_proc(as, :read)
106
108
  # TODO: handle eof
107
109
  ret = Array.new(num_elements) { conv.call(stream) }
@@ -123,7 +125,7 @@ module MemoryIO
123
125
  #
124
126
  # A +Proc+ is allowed, which should accept +stream+ and one object as arguments.
125
127
  #
126
- # If +objects+ is a descendent instance of {Types::Type} and +as+ is +nil,
128
+ # If +objects+ is a descendant instance of {Types::Type} and +as+ is +nil,
127
129
  # +objects.class+ will be used for +as+.
128
130
  # Otherwise, when +as = nil+, this method will simply call +stream.write(objects)+.
129
131
  #
@@ -163,6 +165,7 @@ module MemoryIO
163
165
  stream.pos = from if from
164
166
  as ||= objects.class if objects.class.ancestors.include?(MemoryIO::Types::Type)
165
167
  return stream.write(objects) if as.nil?
168
+
166
169
  conv = to_proc(as, :write)
167
170
  Array(objects).map { |o| conv.call(stream, o) }
168
171
  end
@@ -180,10 +183,12 @@ module MemoryIO
180
183
  # @api private
181
184
  def to_proc(as, rw)
182
185
  ret = as.respond_to?(rw) ? as.method(rw) : as
183
- ret = ret.respond_to?(:call) ? ret : MemoryIO::Types.get_proc(ret, rw)
186
+ ret = MemoryIO::Types.get_proc(ret, rw) unless ret.respond_to?(:call)
184
187
  raise ArgumentError, <<-EOERR.strip unless ret.respond_to?(:call)
188
+
185
189
  Invalid argument `as`: #{as.inspect}. It should be either a Proc or a supported type of MemoryIO::Types.
186
190
  EOERR
191
+
187
192
  ret
188
193
  end
189
194
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MemoryIO
2
4
  # Records information of a process.
3
5
  class Process
@@ -14,11 +16,6 @@ module MemoryIO
14
16
  #
15
17
  # @note
16
18
  # This class only supports procfs-based system. i.e. /proc is mounted and readable.
17
- #
18
- # @todo
19
- # Support MacOS
20
- # @todo
21
- # Support Windows
22
19
  def initialize(pid)
23
20
  @pid = pid
24
21
  @mem = "/proc/#{pid}/mem"
@@ -26,6 +23,7 @@ module MemoryIO
26
23
  @perm = MemoryIO::Util.file_permission(@mem)
27
24
  # TODO: raise custom exception
28
25
  raise Errno::ENOENT, @mem if perm.nil?
26
+
29
27
  # FIXME: use logger
30
28
  warn(<<-EOS.strip) unless perm.readable? || perm.writable?
31
29
  You have no permission to read/write this process.
@@ -59,11 +57,13 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
59
57
  def bases
60
58
  file = "/proc/#{@pid}/maps"
61
59
  stat = MemoryIO::Util.file_permission(file)
62
- return {} unless stat && stat.readable?
60
+ return {} unless stat&.readable?
61
+
63
62
  maps = ::IO.binread(file).split("\n").map do |line|
64
63
  # 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257 /lib/x86_64-linux-gnu/libnss_files-2.24.so
65
64
  addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
66
65
  next nil if pathname.nil?
66
+
67
67
  addr = addr.to_i(16)
68
68
  pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
69
69
  pathname = ::File.basename(pathname)
@@ -104,7 +104,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
104
104
  # #=> "\x7fELF"
105
105
  # @see IO#read
106
106
  def read(addr, num_elements, **options)
107
- mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
107
+ mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
108
108
  end
109
109
 
110
110
  # Write objects at +addr+.
@@ -128,7 +128,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
128
128
  # #=> 'BBBBCCCCAAAAAAAA'
129
129
  # @see IO#write
130
130
  def write(addr, objects, **options)
131
- mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
131
+ mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
132
132
  end
133
133
 
134
134
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'memory_io/types/type'
2
4
 
3
5
  module MemoryIO
@@ -6,7 +8,7 @@ module MemoryIO
6
8
  module Basic
7
9
  # Register numbers to {Types}.
8
10
  #
9
- # All types registerd by this class are assumed as *little endian*.
11
+ # All types registered by this class are assumed as *little endian*.
10
12
  #
11
13
  # This class registered (un)signed {8, 16, 32, 64)-bit integers and IEEE-754 floating numbers.
12
14
  class Number
@@ -35,8 +37,8 @@ module MemoryIO
35
37
  private
36
38
 
37
39
  def unpack(str)
38
- val = str.unpack(@pack_str).first
39
- val -= (2**(@bytes * 8)) if @signed && val >= (2**(@bytes * 8 - 1))
40
+ val = str.unpack1(@pack_str)
41
+ val -= (2**(@bytes * 8)) if @signed && val >= (2**((@bytes * 8) - 1))
40
42
  val
41
43
  end
42
44
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'memory_io/types/type'
4
5
 
@@ -10,15 +11,17 @@ module MemoryIO
10
11
  module Clang
11
12
  # A null-terminated string.
12
13
  class CStr < Types::Type
14
+
13
15
  # @api private
14
16
  #
15
17
  # @return [String]
16
18
  # String without null byte.
17
19
  def self.read(stream)
18
- ret = ''
20
+ ret = +''
19
21
  loop do
20
22
  c = stream.read(1)
21
23
  break if c.nil? || c == '' || c == "\x00"
24
+
22
25
  ret << c
23
26
  end
24
27
  ret
@@ -30,7 +33,7 @@ module MemoryIO
30
33
  # A null byte would be appended if +val+ not ends with null byte.
31
34
  def self.write(stream, val)
32
35
  val = val.to_s
33
- val << "\x00" unless val.end_with?("\x00")
36
+ val += "\x00" unless val.end_with?("\x00")
34
37
  stream.write(val)
35
38
  end
36
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'memory_io/types/type'
2
4
 
3
5
  module MemoryIO
@@ -16,12 +18,11 @@ module MemoryIO
16
18
  # }
17
19
  # };
18
20
  class String < MemoryIO::Types::Type
21
+
19
22
  # std::string uses inlined-buffer if string length isn't larger than {LOCAL_CAPACITY}.
20
23
  LOCAL_CAPACITY = 15
21
24
 
22
- attr_reader :data # @return [::String]
23
- attr_reader :capacity # @return [Integer]
24
- attr_reader :dataplus # @return [Integer]
25
+ attr_reader :data, :capacity, :dataplus # @return [::String] # @return [Integer] # @return [Integer]
25
26
 
26
27
  # Instantiate a {CPP::String} object.
27
28
  #
@@ -30,6 +31,7 @@ module MemoryIO
30
31
  # @param [Integer] dataplus
31
32
  # A pointer.
32
33
  def initialize(data, capacity, dataplus)
34
+ super()
33
35
  @data = data
34
36
  @capacity = capacity
35
37
  @dataplus = dataplus
@@ -54,15 +56,14 @@ module MemoryIO
54
56
  # Custom inspect view.
55
57
  #
56
58
  # @return [String]
57
- #
58
- # @todo
59
- # Let it be colorful in pry.
60
59
  def inspect
60
+ # rubocop:disable Lint/FormatParameterMismatch
61
61
  format("#<%s @data=%s, @capacity=%d, @dataplus=0x%0#{SIZE_T * 2}x>",
62
62
  self.class.name,
63
63
  data.inspect,
64
64
  capacity,
65
65
  dataplus)
66
+ # rubocop:enable Lint/FormatParameterMismatch
66
67
  end
67
68
 
68
69
  class << self
@@ -106,10 +107,10 @@ module MemoryIO
106
107
  write_size_t(stream, obj.length)
107
108
  pos = stream.pos
108
109
  if obj.length > LOCAL_CAPACITY
109
- keep_pos(stream, pos: obj.dataplus) { |s| s.write(obj.data + "\x00") }
110
+ keep_pos(stream, pos: obj.dataplus) { |s| s.write("#{obj.data}\u0000") }
110
111
  write_size_t(stream, obj.capacity)
111
112
  else
112
- stream.write(obj.data + "\x00")
113
+ stream.write("#{obj.data}\u0000")
113
114
  end
114
115
  stream.pos = pos + LOCAL_CAPACITY + 1
115
116
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MemoryIO
2
4
  module Types
3
5
  # @api private
@@ -21,8 +23,8 @@ module MemoryIO
21
23
  # @option [Thread::Backtrace::Location] caller
22
24
  # This option should present if and only if +object+ is a subclass of {Types::Type}.
23
25
  # @option [String] doc
24
- # Docstring.
25
- # Automatically parse from caller location if this parameter isn't present.
26
+ # Doc-string.
27
+ # Automatically parse from caller location if this parameter doesn't present.
26
28
  def initialize(object, keys, option = {})
27
29
  @obj = object
28
30
  @keys = keys
@@ -39,6 +41,7 @@ module MemoryIO
39
41
  def doc
40
42
  return @force_doc if @force_doc
41
43
  return '' unless @caller
44
+
42
45
  parse_file_doc(@caller.absolute_path, @caller.lineno)
43
46
  end
44
47
 
@@ -47,21 +50,24 @@ module MemoryIO
47
50
  # @return [String]
48
51
  def parse_file_doc(file, lineno)
49
52
  return '' unless ::File.file?(file)
53
+
50
54
  strings = []
51
55
  lines = ::IO.binread(file).split("\n")
52
56
  (lineno - 1).downto(1) do |no|
53
57
  str = lines[no - 1]
54
58
  break if str.nil?
59
+
55
60
  str.strip!
56
61
  break unless str.start_with?('#')
57
- strings.unshift(str[2..-1] || '')
62
+
63
+ strings.unshift(str[2..] || '')
58
64
  end
59
65
  trim_docstring(strings)
60
66
  end
61
67
 
62
68
  def trim_docstring(strings)
63
69
  strings = strings.drop_while { |s| s.start_with?('@') }.take_while { |s| !s.start_with?('@') }
64
- strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n") + "\n"
70
+ "#{strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n")}\n"
65
71
  end
66
72
  end
67
73
  end
@@ -1,11 +1,11 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'memory_io/types/record'
4
4
  require 'memory_io/util'
5
5
 
6
6
  module MemoryIO
7
7
  module Types
8
- # The base class, all descendents of this class would be consider as a valid 'type'.
8
+ # The base class, all descendants of this class would be consider as a valid 'type'.
9
9
  class Type
10
10
  # The size of +size_t+. i.e. +sizeof(size_t)+.
11
11
  SIZE_T = 8
@@ -92,7 +92,7 @@ module MemoryIO
92
92
  # Register a new type.
93
93
  #
94
94
  # @param [#read, #write] object
95
- # Normally, +object+ is a descendent class of {Type}.
95
+ # Normally, +object+ is a descendant class of {Type}.
96
96
  #
97
97
  # @option [Symbol, Array<Symbol>] alias
98
98
  # Custom symbol name(s) that can be used in {.find}.
@@ -121,15 +121,17 @@ module MemoryIO
121
121
  #
122
122
  # @see .find
123
123
  def register(object, option = {})
124
- @map ||= OpenStruct.new
124
+ @map ||= {}
125
125
  aliases = Array(option[:alias])
126
126
  reg_fail = ArgumentError.new(<<-EOS.strip)
127
127
  Register '#{object.inspect}' fails because another object with same name has been registered.
128
128
  Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
129
129
  EOS
130
130
  raise reg_fail if aliases.any? && aliases.all? { |ali| @map[ali] }
131
+
131
132
  keys = get_keys(object).concat(aliases).uniq.reject { |k| @map[k] }
132
133
  raise reg_fail if keys.empty?
134
+
133
135
  rec = MemoryIO::Types::Record.new(object, keys, option)
134
136
  keys.each { |k| @map[k] = rec }
135
137
  end
@@ -148,6 +150,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
148
150
  #
149
151
  # To record descendants.
150
152
  def inherited(klass)
153
+ super
151
154
  register(klass, caller: caller_locations(1, 1).first)
152
155
  end
153
156
 
@@ -156,6 +159,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
156
159
  # @return [Array<Symbol>]
157
160
  def get_keys(klass)
158
161
  return [] unless klass.instance_of?(Class)
162
+
159
163
  snake = MemoryIO::Util.underscore(klass.name)
160
164
  snake.gsub!(%r[^memory_io/types/], '')
161
165
  ret = [snake]
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'memory_io/types/type'
2
4
  require 'memory_io/util'
3
5
 
4
- Dir[File.join(__dir__, '**', '*.rb')].each { |f| require f }
6
+ Dir.glob(File.join(__dir__, '**', '*.rb')).each { |f| require f unless f == __FILE__ }
5
7
 
6
8
  module MemoryIO
7
9
  # Module that includes multiple types.
@@ -31,7 +33,7 @@ module MemoryIO
31
33
  # #=> #<MemoryIO::Types::Number:0x000055ecc017a310 @bytes=8, @pack_str="Q", @signed=false>
32
34
  def find(name)
33
35
  obj = Types::Type.find(name)
34
- return obj.obj if obj
36
+ obj&.obj
35
37
  end
36
38
 
37
39
  # @api private
@@ -53,7 +55,7 @@ module MemoryIO
53
55
  # #=> #<Method: MemoryIO::Types::Number#read>
54
56
  def get_proc(name, rw)
55
57
  klass = find(name)
56
- klass && klass.method(rw)
58
+ klass&.method(rw)
57
59
  end
58
60
  end
59
61
  end
@@ -1,9 +1,35 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'dentaku'
3
4
 
4
5
  module MemoryIO
5
6
  # Defines utility methods.
6
7
  module Util
8
+ # A simple class to be returned for getting file permissions.
9
+ class FilePermission
10
+ attr_reader :readable, :writable
11
+ # Alias the two methods to fit Ruby's method naming convention.
12
+ alias readable? readable
13
+ alias writable? writable
14
+
15
+ def initialize(file)
16
+ stat = File.stat(file)
17
+ @readable = stat.readable_real?
18
+ @writable = stat.writable_real?
19
+ # we do a trick here because /proc/[pid]/mem might be marked as writeable but fails at sysopen.
20
+ begin
21
+ @readable && File.open(file, 'rb').close
22
+ rescue Errno::EACCES
23
+ @readable = false
24
+ end
25
+ begin
26
+ @writable && File.open(file, 'wb').close
27
+ rescue Errno::EACCES
28
+ @writable = false
29
+ end
30
+ end
31
+ end
32
+
7
33
  module_function
8
34
 
9
35
  # Convert input into snake-case.
@@ -24,6 +50,7 @@ module MemoryIO
24
50
  # #=> 'my_module/my_class'
25
51
  def underscore(str)
26
52
  return '' if str.empty?
53
+
27
54
  str = str.gsub('::', '/')
28
55
  str.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29
56
  str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
@@ -36,25 +63,12 @@ module MemoryIO
36
63
  # @param [String] file
37
64
  # File name.
38
65
  #
39
- # @return [#readable?, #writable?, nil]
40
- # Struct with two boolean method.
41
- # +nil+ for file not exists or is inaccessible.
66
+ # @return [MemoryIO::Util::FilePermission?]
67
+ # +nil+ is returned if file does not exist or is inaccessible.
42
68
  def file_permission(file)
43
69
  return nil unless File.file?(file)
44
- stat = File.stat(file)
45
- # we do a trick here because /proc/[pid]/mem might be marked as readable but fails at sysopen.
46
- os = OpenStruct.new(readable?: stat.readable_real?, writable?: stat.writable_real?)
47
- begin
48
- os.readable? && File.open(file, 'rb').close
49
- rescue Errno::EACCES
50
- os[:readable?] = false
51
- end
52
- begin
53
- os.writable? && File.open(file, 'wb').close
54
- rescue Errno::EACCES
55
- os[:writable?] = false
56
- end
57
- os
70
+
71
+ FilePermission.new(file)
58
72
  end
59
73
 
60
74
  # Evaluate string safely.
@@ -72,6 +86,7 @@ module MemoryIO
72
86
  # #=> 56960 # 0xde80
73
87
  def safe_eval(str, **vars)
74
88
  return str if str.is_a?(Integer)
89
+
75
90
  # dentaku 2 doesn't support hex
76
91
  str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
77
92
  Dentaku::Calculator.new.store(vars).evaluate(str)
@@ -92,7 +107,7 @@ module MemoryIO
92
107
  # Util.unpack("@\xE2\x01\x00")
93
108
  # #=> 123456
94
109
  def unpack(str)
95
- str.bytes.reverse.reduce(0) { |s, c| s * 256 + c }
110
+ str.bytes.reverse.reduce(0) { |s, c| (s * 256) + c }
96
111
  end
97
112
 
98
113
  # Pack an integer into +b+ bytes.
@@ -130,9 +145,13 @@ module MemoryIO
130
145
  # #=> 'libcrypto'
131
146
  # Util.trim_libname('not_a_so')
132
147
  # #=> 'not_a_so'
148
+ # Util.trim_libname('ld-linux-x86-64.so.2')
149
+ # #=> 'ld'
133
150
  def trim_libname(name)
151
+ return 'ld' if name.start_with?('ld-')
152
+
134
153
  type1 = '(-[\d.]+)?\.so$'
135
- type2 = '\.so.\d+[\d.]+$'
154
+ type2 = '\.so.[\.\d]+$'
136
155
  name.sub(/#{type1}|#{type2}/, '')
137
156
  end
138
157
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MemoryIO
2
4
  # Current gem version.
3
- VERSION = '0.1.1'.freeze
5
+ VERSION = '0.3.0'
4
6
  end
data/lib/memory_io.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # MemoryIO - Read/Write structures in memory.
2
4
  #
3
5
  # @author david942j
metadata CHANGED
@@ -1,43 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memory_io
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2018-01-30 00:00:00.000000000 Z
10
+ date: 2025-11-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: dentaku
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - ">="
16
+ - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 2.0.11
18
+ version: '3'
20
19
  type: :runtime
21
20
  prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ostruct
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.6'
33
+ type: :development
34
+ prerelease: false
22
35
  version_requirements: !ruby/object:Gem::Requirement
23
36
  requirements:
24
37
  - - ">="
25
38
  - !ruby/object:Gem::Version
26
- version: 2.0.11
39
+ version: '0.6'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: rake
29
42
  requirement: !ruby/object:Gem::Requirement
30
43
  requirements:
31
44
  - - "~>"
32
45
  - !ruby/object:Gem::Version
33
- version: '12.0'
46
+ version: '13.0'
34
47
  type: :development
35
48
  prerelease: false
36
49
  version_requirements: !ruby/object:Gem::Requirement
37
50
  requirements:
38
51
  - - "~>"
39
52
  - !ruby/object:Gem::Version
40
- version: '12.0'
53
+ version: '13.0'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: rspec
43
56
  requirement: !ruby/object:Gem::Requirement
@@ -58,28 +71,28 @@ dependencies:
58
71
  requirements:
59
72
  - - "~>"
60
73
  - !ruby/object:Gem::Version
61
- version: '0.52'
74
+ version: '1'
62
75
  type: :development
63
76
  prerelease: false
64
77
  version_requirements: !ruby/object:Gem::Requirement
65
78
  requirements:
66
79
  - - "~>"
67
80
  - !ruby/object:Gem::Version
68
- version: '0.52'
81
+ version: '1'
69
82
  - !ruby/object:Gem::Dependency
70
83
  name: simplecov
71
84
  requirement: !ruby/object:Gem::Requirement
72
85
  requirements:
73
86
  - - "~>"
74
87
  - !ruby/object:Gem::Version
75
- version: 0.13.0
88
+ version: '0.22'
76
89
  type: :development
77
90
  prerelease: false
78
91
  version_requirements: !ruby/object:Gem::Requirement
79
92
  requirements:
80
93
  - - "~>"
81
94
  - !ruby/object:Gem::Version
82
- version: 0.13.0
95
+ version: '0.22'
83
96
  - !ruby/object:Gem::Dependency
84
97
  name: yard
85
98
  requirement: !ruby/object:Gem::Requirement
@@ -94,13 +107,16 @@ dependencies:
94
107
  - - "~>"
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0.9'
97
- description: ''
110
+ description: 'Read/Write complicated structures in memory easily.
111
+
112
+ '
98
113
  email:
99
114
  - david942j@gmail.com
100
115
  executables: []
101
116
  extensions: []
102
117
  extra_rdoc_files: []
103
118
  files:
119
+ - LICENSE
104
120
  - README.md
105
121
  - lib/memory_io.rb
106
122
  - lib/memory_io/io.rb
@@ -116,8 +132,8 @@ files:
116
132
  homepage: https://github.com/david942j/memory_io
117
133
  licenses:
118
134
  - MIT
119
- metadata: {}
120
- post_install_message:
135
+ metadata:
136
+ rubygems_mfa_required: 'true'
121
137
  rdoc_options: []
122
138
  require_paths:
123
139
  - lib
@@ -125,16 +141,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
141
  requirements:
126
142
  - - ">="
127
143
  - !ruby/object:Gem::Version
128
- version: 2.1.0
144
+ version: '3.2'
129
145
  required_rubygems_version: !ruby/object:Gem::Requirement
130
146
  requirements:
131
147
  - - ">="
132
148
  - !ruby/object:Gem::Version
133
149
  version: '0'
134
150
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.6.14
137
- signing_key:
151
+ rubygems_version: 3.6.2
138
152
  specification_version: 4
139
153
  summary: memory_io
140
154
  test_files: []