class NIFTI::Stream
The Stream
class handles string operations (encoding to and decoding from binary strings).
Attributes
A boolean which reports the relationship between the endianness of the system and the instance string.
An array of warning/error messages that (may) have been accumulated.
A File object to write to
A hash of proper strings (based on endianess) to use for unpacking binary strings.
Our current position in the instance string (used only for decoding).
The endianness of the instance string.
The instance string.
Public Class Methods
Creates a Stream
instance.
Parameters¶ ↑
-
binary
– A binary string. -
string_endian
– Boolean. The endianness of the instance string (true for big endian, false for small endian). -
options
– A hash of parameters.
Options¶ ↑
-
:index
– Fixnum. A position (offset) in the instance string where reading will start.
# File lib/nifti/stream.rb, line 32 def initialize(binary, string_endian, options={}) @string = binary || "" @index = options[:index] || 0 @errors = Array.new self.endian = string_endian end
Public Instance Methods
Decodes a section of the instance string and returns the formatted data. The instance index is offset in accordance with the length read.
Notes¶ ↑
-
If multiple numbers are decoded, these are returned in an array.
Parameters¶ ↑
-
length
– Fixnum. The string length which will be decoded. -
type
– String. The type (vr) of data to decode.
# File lib/nifti/stream.rb, line 53 def decode(length, type) # Check if values are valid: if (@index + length) > @string.length # The index number is bigger then the length of the binary string. # We have reached the end and will return nil. value = nil else if type == "AT" value = decode_tag else # Decode the binary string and return value: value = @string.slice(@index, length).unpack(vr_to_str(type)) # If the result is an array of one element, return the element instead of the array. # If result is contained in a multi-element array, the original array is returned. if value.length == 1 value = value[0] # If value is a string, strip away possible trailing whitespace: # Do this using gsub instead of ruby-core #strip to keep trailing carriage # returns, etc., because that is valid whitespace that we want to have included # (i.e. in extended header data, AFNI writes a \n at the end of it's xml info, # and that must be kept in order to not change the file on writing back out). value.gsub!(/\000*$/, "") if value.respond_to? :gsub! end # Update our position in the string: skip(length) end end return value end
Encodes a value and returns the resulting binary string.
Parameters¶ ↑
-
value
– A custom value (String, Fixnum, etc..) or an array of numbers. -
type
– String. The type (vr) of data to encode.
# File lib/nifti/stream.rb, line 200 def encode(value, type) value = [value] unless value.is_a?(Array) return value.pack(vr_to_str(type)) end
Appends a string with trailling spaces to achieve a target length, and encodes it to a binary string. Returns the binary string. Raises an error if pad option is different than :null or :spaces
Parameters¶ ↑
-
string
– A string to be processed. -
target_length
– Fixnum. The target length of the string that is created. -
pad
– Type of desired padding, either :null or :spaces
# File lib/nifti/stream.rb, line 214 def encode_string_to_length(string, target_length, pad = :null) if pad == :spaces template = "A#{target_length}" elsif pad == :null template = "a#{target_length}" else raise StandardError, "Could not identify padding type #{pad}" end length = string.length if length < target_length return [string].pack(template) elsif length == target_length return [string].pack(@str) else raise "The specified string is longer than the allowed maximum length (String: #{string}, Target length: #{target_length})." end end
Returns the length of the binary instance string.
# File lib/nifti/stream.rb, line 85 def length return @string.length end
Resets the instance string and index.
# File lib/nifti/stream.rb, line 105 def reset @string = "" @index = 0 end
Resets the instance index.
# File lib/nifti/stream.rb, line 112 def reset_index @index = 0 end
Calculates and returns the remaining length of the instance string (from the index position).
# File lib/nifti/stream.rb, line 91 def rest_length length = @string.length - @index return length end
Extracts and returns the remaining part of the instance string (from the index position to the end of the string).
# File lib/nifti/stream.rb, line 98 def rest_string str = @string[@index..(@string.length-1)] return str end
Sets an instance file variable.
Notes¶ ↑
For performance reasons, we enable the Stream
instance to write directly to file, to avoid expensive string operations which will otherwise slow down the write performance.
Parameters¶ ↑
-
file
– A File instance.
# File lib/nifti/stream.rb, line 127 def set_file(file) @file = file end
Converts a data type/vr to an encode/decode string used by the pack/unpack methods, which is returned.
Parameters¶ ↑
-
vr
– String. A data type (value representation).
# File lib/nifti/stream.rb, line 159 def vr_to_str(vr) unless @format[vr] errors << "Warning: Element type #{vr} does not have a reading method assigned to it. Something is not implemented correctly or the DICOM data analyzed is invalid." return @hex else return @format[vr] end end
Private Instance Methods
Determines the relationship between system and string endianness, and sets the instance endian variable.
# File lib/nifti/stream.rb, line 239 def configure_endian if CPU_ENDIAN == @str_endian @equal_endian = true else @equal_endian = false end end
Sets the endianness of the instance string. The relationship between the string endianness and the system endianness, determines which encoding/decoding flags to use.
Parameters¶ ↑
-
string_endian
– Boolean. The endianness of the instance string (true for big endian, false for small endian).
# File lib/nifti/stream.rb, line 363 def endian=(string_endian) @str_endian = string_endian configure_endian set_string_formats set_format_hash set_pad_byte end
Sets the hash which is used to convert data element types (VR) to encode/decode strings accepted by the pack/unpack methods.
# File lib/nifti/stream.rb, line 282 def set_format_hash @format = { "BY" => @by, # Byte/Character (1-byte integers) "US" => @us, # Unsigned short (2 bytes) "SS" => @ss, # Signed short (2 bytes) "UL" => @ul, # Unsigned long (4 bytes) "SL" => @sl, # Signed long (4 bytes) "FL" => @fs, # Floating point single (4 bytes) "FD" => @fd, # Floating point double (8 bytes) "OB" => @by, # Other byte string (1-byte integers) "OF" => @fs, # Other float string (4-byte floating point numbers) "OW" => @us, # Other word string (2-byte integers) "AT" => @hex, # Tag reference (4 bytes) NB: This may need to be revisited at some point... "UN" => @hex, # Unknown information (header element is not recognized from local database) "HEX" => @hex, # HEX # We have a number of VRs that are decoded as string: "AE" => @str, "AS" => @str, "CS" => @str, "DA" => @str, "DS" => @str, "DT" => @str, "IS" => @str, "LO" => @str, "LT" => @str, "PN" => @str, "SH" => @str, "ST" => @str, "TM" => @str, "UI" => @str, "UT" => @str, "STR" => @str } end
Sets the hash which is used to keep track of which bytes to use for padding data elements of various vr which have an odd value length.
# File lib/nifti/stream.rb, line 320 def set_pad_byte @pad_byte = { # Space character: "AE" => "\x20", "AS" => "\x20", "CS" => "\x20", "DA" => "\x20", "DS" => "\x20", "DT" => "\x20", "IS" => "\x20", "LO" => "\x20", "LT" => "\x20", "PN" => "\x20", "SH" => "\x20", "ST" => "\x20", "TM" => "\x20", "UT" => "\x20", # Zero byte: "AT" => "\x00", "FL" => "\x00", "FD" => "\x00", "OB" => "\x00", "OF" => "\x00", "OW" => "\x00", "SL" => "\x00", "SQ" => "\x00", "SS" => "\x00", "UI" => "\x00", "UL" => "\x00", "UN" => "\x00", "US" => "\x00" } @pad_byte.default = "\20" end
Sets the pack/unpack format strings that is used for encoding/decoding. Some of these depends on the endianness of the system and the String.
# File lib/nifti/stream.rb, line 255 def set_string_formats if @equal_endian # Native byte order: @us = "S*" # Unsigned short (2 bytes) @ss = "s*" # Signed short (2 bytes) @ul = "I*" # Unsigned long (4 bytes) @sl = "l*" # Signed long (4 bytes) @fs = "e*" # Floating point single (4 bytes) @fd = "E*" # Floating point double ( 8 bytes) else # Network byte order: @us = "n*" @ss = CUSTOM_SS # Custom string for our redefined pack/unpack. @ul = "N*" @sl = CUSTOM_SL # Custom string for our redefined pack/unpack. @fs = "g*" @fd = "G*" end # Format strings that are not dependent on endianness: @by = "C*" # Unsigned char (1 byte) @str = "a*" @hex = "H*" # (this may be dependent on endianness(?)) end