# frozen_string_literal: true # (c) 2018 Ribose Inc. require 'English' require 'ffi' require 'rnp/error' require 'rnp/ffi/librnp' require 'rnp/utils' class Rnp # Class used to feed data out of RNP. # # @note When dealing with very large data, prefer {to_path} which should # be the most efficient. {to_io} is likely to have more overhead. # # @example output to a string # output = Rnp::Input.to_string('my data') # # ... after performing operations # output.string # # @example output to a file # Rnp::Input.to_path('/path/to/my/file') # # @example output to a Ruby IO object # Rnp::Input.to_io(File.open('/path/to/file', 'wb')) class Output # @api private attr_reader :ptr # @api private def initialize(ptr, writer = nil) raise Rnp::Error, 'NULL pointer' if ptr.null? @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy)) @writer = writer end # @api private def self.destroy(ptr) LibRnp.rnp_output_destroy(ptr) end def inspect Rnp.inspect_ptr(self) end # Create an Output to write to a string. # # The resulting string can later be retrieved with {#string}. # # @param max_alloc [Integer] the maximum amount of memory to allocate, # or 0 for unlimited # @return [Output] def self.to_string(max_alloc = 0) pptr = FFI::MemoryPointer.new(:pointer) Rnp.call_ffi(:rnp_output_to_memory, pptr, max_alloc) Output.new(pptr.read_pointer) end # Create an Output to write to a path. # # @param path [String] the path # @return [Output] def self.to_path(path) pptr = FFI::MemoryPointer.new(:pointer) Rnp.call_ffi(:rnp_output_to_path, pptr, path) Output.new(pptr.read_pointer) end # Create an Output to discard all writes. # # @return [Output] def self.to_null pptr = FFI::MemoryPointer.new(:pointer) Rnp.call_ffi(:rnp_output_to_null, pptr) Output.new(pptr.read_pointer) end # Create an Output to write to an IO object. # # @param io [IO, #write] the IO object # @return [Output] def self.to_io(io) to_callback(io.method(:write)) end # Retrieve the data written. Only valid for #{to_string}. # # @return [String, nil] def string pptr = FFI::MemoryPointer.new(:pointer) len = FFI::MemoryPointer.new(:size_t) Rnp.call_ffi(:rnp_output_memory_get_buf, @ptr, pptr, len, false) buf = pptr.read_pointer buf.read_bytes(len.read(:size_t)) unless buf.null? end # @api private WRITER = lambda do |writer, _ctx, buf, buf_len| begin data = buf.read_bytes(buf_len) written = writer.call(data) return written == data.bytesize rescue puts $ERROR_INFO return false end end # @api private def self.to_callback(writer) pptr = FFI::MemoryPointer.new(:pointer) writercb = WRITER.curry[writer] Rnp.call_ffi(:rnp_output_to_callback, pptr, writercb, nil, nil) Output.new(pptr.read_pointer, writercb) end # @api private def self.default(output) to_str = output.nil? output = Output.to_string if to_str yield output output.string if to_str end end # class end # class