Module: Proj::FileApiCallbacks
- Defined in:
- lib/proj/file_api_callbacks.rb
Overview
Include this module in a class to create a custom file API for PROJ. Install it via Context#set_file_api.
The including class must call #install_callbacks in its initializer and implement the following methods:
-
open(path, access_mode) - Open a file.
access_modeis one of:PROJ_OPEN_ACCESS_READ_ONLY,:PROJ_OPEN_ACCESS_READ_UPDATE, or:PROJ_OPEN_ACCESS_CREATE. Return a file object (any Ruby object) or nil on error. -
read(file, size_bytes) - Read up to
size_bytesfromfile, return a String. -
write(file, data) - Write
datatofile, return the number of bytes written. -
seek(file, offset, whence) - Seek within
fileusing SEEK_SET/SEEK_CUR/SEEK_END. -
tell(file) - Return the current position in
file. -
close(file) - Close
file. -
exists(path) - Return true if the file at
pathexists. -
mkdir(path) - Create directory at
path, return true on success. -
unlink(path) - Remove file at
path, return true on success. -
rename(original_path, new_path) - Rename a file, return true on success.
The file parameter passed to read/write/seek/tell/close is whatever object your open method returned.
Instance Method Summary collapse
-
#close_callback(context, handle, user_data) ⇒ Object
Close file.
-
#exists_callback(context, path, user_data) ⇒ Object
Return TRUE if a file exists.
- #handle_to_file(handle) ⇒ Object
- #install_callbacks(context) ⇒ Object
-
#mkdir_callback(context, path, user_data) ⇒ Object
Return TRUE if directory exists or could be created.
-
#open_callback(context, path, access_mode, user_data) ⇒ Object
Open file.
-
#read_callback(context, handle, buffer, size_bytes, user_data) ⇒ Object
Read sizeBytes into buffer from current position and return number of bytes read.
-
#register_handle(file) ⇒ Object
Create an opaque handle for PROJ and associate it with a file object.
-
#rename_callback(context, original_path, new_path, user_data) ⇒ Object
Return TRUE if file could be renamed.
-
#seek_callback(context, handle, offset, whence, user_data) ⇒ Object
Seek to offset using whence=SEEK_SET/SEEK_CUR/SEEK_END.
-
#tell_callback(context, handle, user_data) ⇒ Object
Return current file position.
-
#unlink_callback(context, path, user_data) ⇒ Object
Return TRUE if file could be removed.
- #unregister_handle(handle) ⇒ Object
-
#write_callback(context, handle, buffer, size_bytes, user_data) ⇒ Object
Write sizeBytes into buffer from current position and return number of bytes written.
Instance Method Details
#close_callback(context, handle, user_data) ⇒ Object
Close file
107 108 109 110 111 |
# File 'lib/proj/file_api_callbacks.rb', line 107 def close_callback(context, handle, user_data) file = handle_to_file(handle) self.close(file) unregister_handle(handle) end |
#exists_callback(context, path, user_data) ⇒ Object
Return TRUE if a file exists
114 115 116 117 118 119 120 |
# File 'lib/proj/file_api_callbacks.rb', line 114 def exists_callback(context, path, user_data) if self.exists(path) 1 else 0 end end |
#handle_to_file(handle) ⇒ Object
157 158 159 |
# File 'lib/proj/file_api_callbacks.rb', line 157 def handle_to_file(handle) @file_api_handles[handle.address][:file] end |
#install_callbacks(context) ⇒ Object
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 |
# File 'lib/proj/file_api_callbacks.rb', line 40 def install_callbacks(context) # PROJ keeps using this structure after proj_context_set_fileapi returns, # so it must be retained on the Ruby object to avoid GC invalidating it. @proj_file_api = Api::ProjFileApi.new @proj_file_api[:version] = 1 # Maps native address -> {proj_handle:, file:}. Retaining proj_handle # prevents the MemoryPointer from being GCed while PROJ holds the address. @file_api_handles = {} @proj_file_api[:open_cbk] = self.method(:open_callback) @proj_file_api[:read_cbk] = self.method(:read_callback) @proj_file_api[:write_cbk] = self.method(:write_callback) @proj_file_api[:seek_cbk] = self.method(:seek_callback) @proj_file_api[:tell_cbk] = self.method(:tell_callback) @proj_file_api[:close_cbk] = self.method(:close_callback) @proj_file_api[:exists_cbk] = self.method(:exists_callback) @proj_file_api[:mkdir_cbk] = self.method(:mkdir_callback) @proj_file_api[:unlink_cbk] = self.method(:unlink_callback) @proj_file_api[:rename_cbk] = self.method(:rename_callback) result = Api.proj_context_set_fileapi(context, @proj_file_api, nil) if result != 1 Error.check_object(self) end end |
#mkdir_callback(context, path, user_data) ⇒ Object
Return TRUE if directory exists or could be created
123 124 125 126 127 128 129 |
# File 'lib/proj/file_api_callbacks.rb', line 123 def mkdir_callback(context, path, user_data) if self.mkdir(path) 1 else 0 end end |
#open_callback(context, path, access_mode, user_data) ⇒ Object
Open file. Return NULL if error
69 70 71 72 73 |
# File 'lib/proj/file_api_callbacks.rb', line 69 def open_callback(context, path, access_mode, user_data) file = self.open(path, access_mode) return nil unless file register_handle(file) end |
#read_callback(context, handle, buffer, size_bytes, user_data) ⇒ Object
Read sizeBytes into buffer from current position and return number of bytes read
76 77 78 79 80 81 82 83 84 |
# File 'lib/proj/file_api_callbacks.rb', line 76 def read_callback(context, handle, buffer, size_bytes, user_data) file = handle_to_file(handle) data = self.read(file, size_bytes) return 0 if data.nil? || data.empty? read_bytes = [size_bytes, data.bytesize].min buffer.put_bytes(0, data, 0, read_bytes) read_bytes end |
#register_handle(file) ⇒ Object
Create an opaque handle for PROJ and associate it with a file object. The MemoryPointer is retained to prevent GC while PROJ holds the address.
151 152 153 154 155 |
# File 'lib/proj/file_api_callbacks.rb', line 151 def register_handle(file) proj_handle = FFI::MemoryPointer.new(:pointer) @file_api_handles[proj_handle.address] = { proj_handle: proj_handle, file: file } proj_handle end |
#rename_callback(context, original_path, new_path, user_data) ⇒ Object
Return TRUE if file could be renamed
141 142 143 144 145 146 147 |
# File 'lib/proj/file_api_callbacks.rb', line 141 def rename_callback(context, original_path, new_path, user_data) if self.rename(original_path, new_path) 1 else 0 end end |
#seek_callback(context, handle, offset, whence, user_data) ⇒ Object
Seek to offset using whence=SEEK_SET/SEEK_CUR/SEEK_END. Return TRUE in case of success
94 95 96 97 98 |
# File 'lib/proj/file_api_callbacks.rb', line 94 def seek_callback(context, handle, offset, whence, user_data) file = handle_to_file(handle) self.seek(file, offset, whence) return 1 # True end |
#tell_callback(context, handle, user_data) ⇒ Object
Return current file position
101 102 103 104 |
# File 'lib/proj/file_api_callbacks.rb', line 101 def tell_callback(context, handle, user_data) file = handle_to_file(handle) self.tell(file) end |
#unlink_callback(context, path, user_data) ⇒ Object
Return TRUE if file could be removed
132 133 134 135 136 137 138 |
# File 'lib/proj/file_api_callbacks.rb', line 132 def unlink_callback(context, path, user_data) if self.unlink(path) 1 else 0 end end |
#unregister_handle(handle) ⇒ Object
161 162 163 |
# File 'lib/proj/file_api_callbacks.rb', line 161 def unregister_handle(handle) @file_api_handles.delete(handle.address) end |
#write_callback(context, handle, buffer, size_bytes, user_data) ⇒ Object
Write sizeBytes into buffer from current position and return number of bytes written
87 88 89 90 91 |
# File 'lib/proj/file_api_callbacks.rb', line 87 def write_callback(context, handle, buffer, size_bytes, user_data) file = handle_to_file(handle) data = buffer.get_bytes(0, size_bytes) self.write(file, data) end |