lib/ffi/libfuse/fuse_buffer.rb in ffi-libfuse-0.0.1.rctest12 vs lib/ffi/libfuse/fuse_buffer.rb in ffi-libfuse-0.1.0.rc20220550

- old
+ new

@@ -12,11 +12,10 @@ # Single data buffer # # Generic data buffer for I/O, extended attributes, etc...Data may be supplied as a memory pointer or as a file # descriptor # - # @todo define helper methods to create buffers pointing to file_descriptors or allocated memory class FuseBuf < FFI::Struct layout( size: :size_t, # Size of data in bytes flags: :fuse_buf_flags, # Buffer flags mem: :pointer, # Memory pointer - used if :is_fd flag is not set @@ -24,23 +23,64 @@ pos: :off_t # File position - used if :fd_seek flag is set. ) # rubocop:disable Naming/MethodParameterName - # @param [Integer] size Size of data in bytes - # @param [Integer] fd File descriptor - # @param [FFI::Pointer] mem Memory pointer - # @param [Boolean] fd_retry - # Retry operation on file descriptor + # @!attribute [r] mem + # @return [FFI::Pointer] the memory in the buffer + def mem + self[:mem] + end + alias memory mem + + # @!attribute [r] fd + # @return [Integer] the file descriptor number + def fd + self[:fd] + end + alias file_descriptor fd + + # @return [Boolean] true if this a memory buffer + def mem? + !fd? + end + + # @return [Boolean] true if this is a file descriptor buffer + def fd? + self[:flags].include?(:is_fd) + end + alias file_descriptor? fd? + + # Resize mem to smaller than initially allocated + # @param [Integer] new_size + # @return [void] + def resize(new_size) + self[:size] = new_size + end + + # @overload fill(size:, mem:, auto_release) + # Fill as a Memory buffer + # @param [Integer] size Size of data in bytes + # @param [FFI::Pointer] mem Memory pointer allocated to size if required + # @param [Boolean] autorelease # - # If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error or - # EOF is detected. + # @overload fill(fd:, fd_retry:, pos:) + # Fill as a FileDescriptor buffer + # @param [Integer] fd File descriptor + # @param [Boolean] fd_retry + # Retry operations on file descriptor # - # @param [Integer] pos - # If > 0 then used to seek to the given offset before performing operation on file descriptor. + # If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error + # or EOF is detected. + # + # @param [Integer] pos + # If > 0 then used to seek to the given offset before performing operation on file descriptor. # @return [self] - def fill(size:, mem: FFI::Pointer::NULL, fd: -1, fd_retry: false, pos: 0) + def fill(mem: FFI::Pointer::NULL, size: mem.null? ? 0 : mem.size, fd: -1, fd_retry: false, pos: 0) + mem = FFI::MemoryPointer.new(:char, size, true) if fd == -1 && mem.null? && size.positive? + mem.autorelease = to_ptr.autorelease? unless mem.null? + self[:size] = size self[:mem] = mem self[:fd] = fd flags = [] flags << :is_fd if fd != -1 @@ -58,51 +98,90 @@ # # An array of data buffers, each containing a memory pointer or a file descriptor. # # Allocate dynamically to add more than one buffer. # - # @todo find a use for {FuseOperations#read_buf} and implement necessary helpers class FuseBufVec < FFI::Struct layout( count: :size_t, idx: :size_t, off: :size_t, - buf: :pointer + # but is treated as a variable length array of FuseBuf at size +1 following the struct. + buf: [FuseBuf, 1] # struct fuse_buf[1] ) + # @!attribute [r] count - # @todo implement # @return [Integer] the number of buffers in the array + def count + self[:count] + end # @!attribute [r] index - # @todo implement # @return [Integer] index of current buffer within the array + def index + self[:idx] + end + alias idx index # @!attribute [r] offset - # @todo implement # @return [Integer] current offset within the current buffer + def offset + self[:off] + end + alias off offset # @!attribute [r] buffers - # @todo implement # @return [Array<FuseBuf>] array of buffers + def buffers + @buffers ||= Array.new(count) do |i| + next self[:buf].first if i.zero? - # @see #init - def self.init(**buf_options) - new.init(**buf_options) + FuseBuf.new(self[:buf].to_ptr + (i * FuseBuf.size)) + end end - # Allocate a vector containing a single buffer + # @!attribute [r] current + # @return [FuseBuf] the current buffer + def current + return self[:buf].first if index.zero? + + FuseBuf.new(self[:buf].to_ptr + (index * FuseBuf.size)) + end + + # Create and initialise a new FuseBufVec + # @param [Boolean] autorelease should the struct be freed on GC (default NO!!!) + # @param [Hash] buf_options options for configuring the initial buffer. See {#init} + # @yield(buf,index) + # @yieldparam [FuseBuf] buf + # @yieldparam [Integer] index + # @yieldreturn [void] + # @return [FuseBufVec] + def self.init(autorelease: true, count: 1, **buf_options) + bufvec_ptr = FFI::MemoryPointer.new(:uchar, FuseBufVec.size + (FuseBuf.size * (count - 1)), true) + bufvec_ptr.autorelease = autorelease + bufvec = new(bufvec_ptr) + bufvec[:count] = count + bufvec.init(**buf_options) + + buffers.each_with_index { |b, i| yield i, b } if block_given? + bufvec + end + + # Set and initialise a specific buffer # # See fuse_common.h FUSE_BUFVEC_INIT macro - # @param [Hash<Symbol,Object>] buf_options see {FuseBuf.fill} - def init(**buf_options) - self[:count] = 1 - self[:idx] = 0 + # @param [Integer] index the index of the buffer + # @param [Hash<Symbol,Object>] buf_options see {FuseBuf#fill} + # @return [FuseBuf] the initial buffer + def init(index: 0, **buf_options) + self[:idx] = index self[:off] = 0 - self[:buf] = FuseBuf.new.fill(**buf_options), to_ptr + current.fill(**buf_options) unless buf_options.empty? self end + # Would pref this to be called #size but clashes with FFI::Struct, might need StructWrapper # @return [Integer] total size of data in a fuse buffer vector def buf_size Libfuse.fuse_buf_size(self) end @@ -138,9 +217,32 @@ # # @return [Integer] actual number of bytes copied or -errno on error # def copy_to(dst, *flags) Libfuse.fuse_buf_copy(dst, self, flags) + end + + # Copy our data direct to file descriptor + # @param [Integer] fileno a file descriptor + # @param [Integer] offset + # @param [Array<Symbol>] flags - see {copy_to} + # @return [Integer] number of bytes copied + def copy_to_fd(fileno, offset = 0, *flags) + dst = self.class.init(size: buf_size, fd: fileno, pos: offset) + copy_to(dst, *flags) + end + + # Copy to string via a temporary buffer + # @param [Array<Symbol>] flags - see {copy_to} + # @return [String] the extracted data + def copy_to_str(*flags) + dst = FuseBufVec.init(size: buf_size) + copied = copy_to(dst, *flags) + dst.current.memory.read_string(copied) + end + + def copy_from(src, *flags) + Libfuse.fuse_buf_copy(self, src, flags) end end attach_function :fuse_buf_size, [FuseBufVec.by_ref], :size_t attach_function :fuse_buf_copy, [FuseBufVec.by_ref, FuseBufVec.by_ref, :fuse_buf_copy_flags], :ssize_t