Class: CTypes::String

Inherits:
Object
  • Object
show all
Includes:
Type
Defined in:
lib/ctypes/string.rb

Overview

Type used to unpack binary strings into Ruby String instances

Examples:

greedy string

t = CTypes::String.new
t.unpack("hello world\0bye")      # => "hello world"

# greedy string will consume all bytes, but only return string up to the
# first null-terminator
t.unpack_one("hello world\0bye")  # => ["hello world", ""]

t.pack("test")                    # => "test"
t.pack("test\0")                  # => "test\0"

null-terminated string

t = CTypes::String.terminated
t.unpack("hello world\0bye")      # => "hello world"

# terminated string will consume bytes up to and including the
# terminator
t.unpack_one("hello world\0bye")  # => ["hello world", "bye"]

t.pack("test")                    # => "test\0"

fixed-size string

t = CTypes::String.new(size: 5)
t.unpack("hello world\0bye")      # => "hello"
t.unpack_one("hello world\0bye")  # => ["hello", " world\0bye"]
t.pack("hi")                      # => "hi\0\0\0"

fixed-size string, preserving null bytes

t = CTypes::String.new(size: 8, trim: false)
t.unpack("abc\0\0xyzXXXX")        # => "abc\0\0xyz"
t.pack("hello")                   # => "hello\0\0\0"

Instance Attribute Summary collapse

Attributes included from Type

#dry_type, #endian

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Type

#default_endian, #default_value, #fixed_size?, #pread, #read, #unpack, #unpack_all, #with_endian, #without_endian

Constructor Details

#initialize(size: nil, trim: true) ⇒ String

Returns a new instance of String.

Parameters:

  • size (Integer) (defaults to: nil)

    number of bytes

  • trim (Boolean) (defaults to: true)

    set to false to preserve null bytes when unpacking



63
64
65
66
67
68
69
70
71
72
# File 'lib/ctypes/string.rb', line 63

def initialize(size: nil, trim: true)
  @size = size
  @trim = trim
  @dry_type = Dry::Types["coercible.string"].default("")
  @dry_type = @dry_type.constrained(max_size: size) if size.is_a?(Integer)
  size ||= "*"

  @fmt_pack = "a%s" % size
  @fmt_unpack = (trim ? "Z%s" : "a%s") % size
end

Instance Attribute Details

#trimObject (readonly)

Returns the value of attribute trim.



73
74
75
# File 'lib/ctypes/string.rb', line 73

def trim
  @trim
end

Class Method Details

.terminated(terminator = "\0") ⇒ Object

Return a CTypes::String type that is terminated by the supplied sequence

Examples:

null-terminated string

t = CTypes::String.terminated
t.unpack("hello world\0bye")    # => "hello world"

string terminated string

t = CTypes::String.terminated("STOP")
t.unpack("test 1STOPtest 2STOP")  # => "test 1"

Parameters:

  • terminator (::String) (defaults to: "\0")

    byte sequence to terminate the string



54
55
56
57
58
59
# File 'lib/ctypes/string.rb', line 54

def self.terminated(terminator = "\0")
  size = terminator.size
  Terminated.new(type: new,
    locate: proc { |b, _| [b.index(terminator), size] },
    terminate: terminator)
end

Instance Method Details

#==(other) ⇒ Object



150
151
152
# File 'lib/ctypes/string.rb', line 150

def ==(other)
  other.is_a?(self.class) && other.size == size && other.trim == trim
end

#export_type(q) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ctypes/string.rb', line 125

def export_type(q)
  q << if size && size > 0
    if trim
      "string(%d)" % [@size]
    else
      "string(%d, trim: false)" % [@size]
    end
  else
    "string"
  end
end

#greedy?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


98
99
100
# File 'lib/ctypes/string.rb', line 98

def greedy?
  @size.nil?
end

#pack(value, endian: default_endian, validate: true) ⇒ ::String

pack a ruby String into a binary string, applying any required padding

Parameters:

  • value (::String)

    string to pack

  • endian (Symbol) (defaults to: default_endian)

    endian to use when packing; ignored

  • validate (Boolean) (defaults to: true)

    set to false to disable validation

Returns:

  • (::String)

    binary encoding for value



81
82
83
84
# File 'lib/ctypes/string.rb', line 81

def pack(value, endian: default_endian, validate: true)
  value = @dry_type[value] if validate
  [value].pack(@fmt_pack)
end

#pretty_print(q) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/ctypes/string.rb', line 111

def pretty_print(q)
  if size && size > 0
    if trim
      q.text("string(%d)" % @size)
    else
      q.text("string(%d, trim: false)" % @size)
    end
  else
    q.text "string"
  end
end

#sizeObject

get the size in bytes of the string; returns 0 for greedy strings



103
104
105
# File 'lib/ctypes/string.rb', line 103

def size
  @size || 0
end

#terminated(terminator = "\0") ⇒ Object

This function is provided as a helper to Helpers#string to enable ‘string.terminated` as a type.

See Also:



146
147
148
# File 'lib/ctypes/string.rb', line 146

def terminated(terminator = "\0")
  String.terminated(terminator)
end

#to_sObject



107
108
109
# File 'lib/ctypes/string.rb', line 107

def to_s
  "string[#{size}]"
end

#type_nameObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



138
139
140
# File 'lib/ctypes/string.rb', line 138

def type_name
  @size ? "char[#{@size}]" : "char[]"
end

#unpack_one(buf, endian: default_endian) ⇒ ::Array(::String, ::String)

unpack a ruby String from binary string data

Parameters:

  • buf (::String)

    bytes that make up the type

  • endian (Symbol) (defaults to: default_endian)

    endian of data within buf

Returns:

  • (::Array(::String, ::String))

    unpacked string, and unused bytes



90
91
92
93
94
95
# File 'lib/ctypes/string.rb', line 90

def unpack_one(buf, endian: default_endian)
  raise missing_bytes_error(input: buf, need: @size) if
    @size && buf.size < @size
  value = buf.unpack1(@fmt_unpack)
  [value, @size ? buf.byteslice(@size..) : ""]
end