File: stringz.rb

package info (click to toggle)
ruby-bindata 2.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 600 kB
  • sloc: ruby: 8,566; makefile: 4
file content (96 lines) | stat: -rw-r--r-- 2,259 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
require "bindata/base_primitive"

module BinData
  # A BinData::Stringz object is a container for a zero ("\0") terminated
  # string.
  #
  # For convenience, the zero terminator is not necessary when setting the
  # value.  Likewise, the returned value will not be zero terminated.
  #
  #   require 'bindata'
  #
  #   data = "abcd\x00efgh"
  #
  #   obj = BinData::Stringz.new
  #   obj.read(data)
  #   obj.snapshot #=> "abcd"
  #   obj.num_bytes #=> 5
  #   obj.to_binary_s #=> "abcd\000"
  #
  # == Parameters
  #
  # Stringz objects accept all the params that BinData::BasePrimitive
  # does, as well as the following:
  #
  # <tt>:max_length</tt>:: The maximum length of the string including the zero
  #                        byte.
  class Stringz < BinData::BasePrimitive

    optional_parameters :max_length

    def assign(val)
      super(binary_string(val))
    end

    def snapshot
      # override to always remove trailing zero bytes
      result = super
      trim_and_zero_terminate(result).chomp("\0")
    end

    #---------------
    private

    def value_to_binary_string(val)
      trim_and_zero_terminate(val)
    end

    def read_and_return_value(io)
      max_length = eval_parameter(:max_length)
      str = ""
      i = 0
      ch = nil

      # read until zero byte or we have read in the max number of bytes
      while ch != "\0" && i != max_length
        ch = io.readbytes(1)
        str += ch
        i += 1
      end

      trim_and_zero_terminate(str)
    end

    def sensible_default
      ""
    end

    def trim_and_zero_terminate(str)
      result = binary_string(str)
      truncate_after_first_zero_byte!(result)
      trim_to!(result, eval_parameter(:max_length))
      append_zero_byte_if_needed!(result)
      result
    end

    def truncate_after_first_zero_byte!(str)
      str.sub!(/([^\0]*\0).*/, '\1')
    end

    def trim_to!(str, max_length = nil)
      if max_length
        max_length = 1 if max_length < 1
        str.slice!(max_length..-1)
        if str.length == max_length && str[-1, 1] != "\0"
          str[-1, 1] = "\0"
        end
      end
    end

    def append_zero_byte_if_needed!(str)
      if str.length == 0 || str[-1, 1] != "\0"
        str << "\0"
      end
    end
  end
end