libcall 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +79 -17
- data/lib/libcall/caller.rb +37 -0
- data/lib/libcall/cli.rb +10 -1
- data/lib/libcall/fiddley.rb +156 -0
- data/lib/libcall/parser.rb +29 -0
- data/lib/libcall/type_map.rb +33 -0
- data/lib/libcall/version.rb +1 -1
- data/lib/libcall.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db2f2234f00412d76e307fc12179b0a829953a08bd6aefa83d59bbab866e85f3
|
|
4
|
+
data.tar.gz: bb2cd9b42fb09fc6b6e5201ed98c16dae47258e9a1fb23ef76fc6a49efe17ab3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 12bc58798f6130a316ba87e53a66d2806035a7fd8abbb6ba2fc71654d82e0f2210149152cf9876ca818a5241808a2c871021eca2c7264a2110cfe37199da2872
|
|
7
|
+
data.tar.gz: bb6f252e8a7a9aec0a353abec0ed9674b346bb1af3471768ca51fad040234024c6fc77b18a25d42c396a389a0063ac44c5675f54fa8bd66eed3b0c9055da8e34
|
data/README.md
CHANGED
|
@@ -23,13 +23,11 @@ libcall [OPTIONS] <LIBRARY> <FUNCTION> (TYPE VALUE)...
|
|
|
23
23
|
### Quick Examples
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
# => 4.0
|
|
26
|
+
libcall -lm sqrt double 16 -r double # => 4.0
|
|
27
|
+
```
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
libcall -lc strlen string "hello" -r usize
|
|
32
|
-
# => 5
|
|
29
|
+
```sh
|
|
30
|
+
libcall -lc strlen string "hello" -r usize # => 5
|
|
33
31
|
```
|
|
34
32
|
|
|
35
33
|
### Argument Syntax
|
|
@@ -76,25 +74,51 @@ Library search:
|
|
|
76
74
|
|
|
77
75
|
### More Examples
|
|
78
76
|
|
|
77
|
+
TYPE/VALUE pairs with `-r` before function
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
libcall -lm -r double fabs double -5.5 # => 5.5
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Output parameter with libm
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
libcall -lm modf double -3.14 out:double -r f64
|
|
87
|
+
# Result: -0.14000000000000012
|
|
88
|
+
# Output parameters:
|
|
89
|
+
# [1] double = -3.0
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
JSON output
|
|
93
|
+
|
|
79
94
|
```sh
|
|
80
|
-
# JSON output
|
|
81
95
|
libcall --json -lm sqrt double 9.0 -r f64
|
|
96
|
+
# {
|
|
97
|
+
# "library": "/lib/x86_64-linux-gnu/libm.so",
|
|
98
|
+
# "function": "sqrt",
|
|
99
|
+
# "return_type": "double",
|
|
100
|
+
# "result": 3.0
|
|
101
|
+
# }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Dry run
|
|
82
105
|
|
|
83
|
-
|
|
106
|
+
```sh
|
|
84
107
|
libcall --dry-run -lc getpid -r int
|
|
108
|
+
# Library: /lib/x86_64-linux-gnu/libc.so
|
|
109
|
+
# Function: getpid
|
|
110
|
+
# Return: int
|
|
111
|
+
```
|
|
85
112
|
|
|
86
|
-
|
|
87
|
-
libcall -lm modf double -3.14 out:double -r f64
|
|
113
|
+
Windows: calling C runtime functions
|
|
88
114
|
|
|
89
|
-
|
|
90
|
-
libcall
|
|
91
|
-
|
|
115
|
+
```powershell
|
|
116
|
+
libcall msvcrt.dll sqrt double 16.0 -r f64 # => 4.0
|
|
117
|
+
```
|
|
92
118
|
|
|
93
|
-
|
|
94
|
-
libcall msvcrt.dll sqrt double 16.0 -r f64
|
|
95
|
-
# => 4.0
|
|
119
|
+
Windows: accessing environment variables
|
|
96
120
|
|
|
97
|
-
|
|
121
|
+
```powershell
|
|
98
122
|
libcall msvcrt.dll getenv string "PATH" -r cstr
|
|
99
123
|
```
|
|
100
124
|
|
|
@@ -119,6 +143,8 @@ Also supported:
|
|
|
119
143
|
- `cstr`: C string return (char\*)
|
|
120
144
|
- `ptr`/`pointer`: void\* pointer
|
|
121
145
|
|
|
146
|
+
See [type_map.rb](lib/libcall/type_map.rb) for all available type mappings.
|
|
147
|
+
|
|
122
148
|
## pkg-config Support
|
|
123
149
|
|
|
124
150
|
Set `PKG_CONFIG_PATH` and use package names with `-l`:
|
|
@@ -160,6 +186,42 @@ libcall -lc getrandom out:uchar[16] size_t 16 uint 0 -r long
|
|
|
160
186
|
libcall -lSystem arc4random_buf out:uchar[16] size_t 16 -r void
|
|
161
187
|
```
|
|
162
188
|
|
|
189
|
+
## Callbacks (experimental)
|
|
190
|
+
|
|
191
|
+
Pass a C function pointer via a Ruby callback. Use `func` or `callback` with a quoted spec:
|
|
192
|
+
|
|
193
|
+
- Syntax: `func 'RET(ARG,ARG,...){|a, b, ...| ruby_code }'` (alias: `callback ...`)
|
|
194
|
+
- Inside the block, helper methods from `Libcall::Fiddley::DSL` are available:
|
|
195
|
+
- `int(ptr)`, `double(ptr)`, `cstr(ptr)` read values from pointers
|
|
196
|
+
- `read(:type, ptr)` reads any supported type; `ptr(addr)` makes a pointer
|
|
197
|
+
|
|
198
|
+
Quick examples
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
# Fixture function: int32_t apply_i32(int32_t, int32_t, int32_t (*)(int32_t,int32_t))
|
|
202
|
+
libcall -ltest -L test/fixtures/libtest/build apply_i32 \
|
|
203
|
+
int 3 int 5 \
|
|
204
|
+
func 'int(int,int){|a,b| a + b}' \
|
|
205
|
+
-r i32
|
|
206
|
+
# => 8
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
# libc qsort: sort 4 ints ascending; use out:int[4] with an initializer so the result prints
|
|
211
|
+
libcall -lc qsort \
|
|
212
|
+
out:int[4] 4,2,3,1 \
|
|
213
|
+
size_t 4 \
|
|
214
|
+
size_t 4 \
|
|
215
|
+
callback 'int(void*,void*){|pa,pb| int(pa) <=> int(pb) }' \
|
|
216
|
+
-r void
|
|
217
|
+
# Output parameters:
|
|
218
|
+
# [0] int[4] = [1, 2, 3, 4]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Notes
|
|
222
|
+
|
|
223
|
+
- Match the C signature exactly (types and arity). Blocks run in-process; exceptions abort the call.
|
|
224
|
+
|
|
163
225
|
## Warning
|
|
164
226
|
|
|
165
227
|
FFI calls are inherently unsafe. You must:
|
data/lib/libcall/caller.rb
CHANGED
|
@@ -18,6 +18,7 @@ module Libcall
|
|
|
18
18
|
arg_types = []
|
|
19
19
|
arg_values = []
|
|
20
20
|
out_refs = []
|
|
21
|
+
closures = []
|
|
21
22
|
|
|
22
23
|
arg_pairs.each_with_index do |(type_sym, value), idx|
|
|
23
24
|
arg_types << TypeMap.to_fiddle_type(type_sym)
|
|
@@ -39,11 +40,47 @@ module Libcall
|
|
|
39
40
|
base = type_sym[1]
|
|
40
41
|
count = type_sym[2]
|
|
41
42
|
ptr = TypeMap.allocate_array(base, count)
|
|
43
|
+
# Optional initializer values
|
|
44
|
+
if value
|
|
45
|
+
vals = Array(value)
|
|
46
|
+
raise Error, "Initializer length #{vals.length} does not match out array size #{count}" unless vals.length == count
|
|
47
|
+
TypeMap.write_array(ptr, base, vals)
|
|
48
|
+
end
|
|
42
49
|
out_refs << { index: idx, kind: :out_array, base: base, count: count, ptr: ptr }
|
|
43
50
|
arg_values << ptr.to_i
|
|
44
51
|
else
|
|
45
52
|
raise Error, "Unknown array/output form: #{type_sym.inspect}"
|
|
46
53
|
end
|
|
54
|
+
elsif type_sym == :callback
|
|
55
|
+
spec = value
|
|
56
|
+
unless spec.is_a?(Hash) && spec[:kind] == :callback
|
|
57
|
+
raise Error, 'Invalid callback value; expected func signature and block'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
ret_ty = TypeMap.to_fiddle_type(spec[:ret])
|
|
61
|
+
arg_tys = spec[:args].map { |a| TypeMap.to_fiddle_type(a) }
|
|
62
|
+
# Build Ruby proc from block source, e.g., "{|a,b| a+b}"
|
|
63
|
+
# Evaluate proc in a helper context so DSL methods are available
|
|
64
|
+
ctx = Object.new.extend(Libcall::Fiddley::DSL)
|
|
65
|
+
begin
|
|
66
|
+
ruby_proc = ctx.instance_eval("proc #{spec[:block]}", __FILE__, __LINE__)
|
|
67
|
+
rescue SyntaxError => e
|
|
68
|
+
raise Error, "Invalid Ruby block for callback: #{e.message}"
|
|
69
|
+
end
|
|
70
|
+
closure = Fiddle::Closure::BlockCaller.new(ret_ty, arg_tys) do |*cb_args|
|
|
71
|
+
# Convert pointer-typed args to Fiddle::Pointer for convenience
|
|
72
|
+
cooked = cb_args.each_with_index.map do |v, i|
|
|
73
|
+
at = spec[:args][i]
|
|
74
|
+
if at == :voidp
|
|
75
|
+
Fiddle::Pointer.new(v)
|
|
76
|
+
else
|
|
77
|
+
v
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
ruby_proc.call(*cooked)
|
|
81
|
+
end
|
|
82
|
+
closures << closure # keep alive during call
|
|
83
|
+
arg_values << closure
|
|
47
84
|
else
|
|
48
85
|
arg_values << value
|
|
49
86
|
end
|
data/lib/libcall/cli.rb
CHANGED
|
@@ -156,8 +156,17 @@ module Libcall
|
|
|
156
156
|
type_sym = Parser.parse_type(type_tok)
|
|
157
157
|
|
|
158
158
|
# TYPE that represents an output pointer/array does not require a value
|
|
159
|
+
# For out:TYPE[N], allow an optional comma-separated initializer list right after the type.
|
|
159
160
|
if type_sym.is_a?(Array) && %i[out out_array].include?(type_sym.first)
|
|
160
|
-
|
|
161
|
+
if type_sym.first == :out_array && i < argv.length && argv[i].include?(',')
|
|
162
|
+
init_tok = argv[i]
|
|
163
|
+
i += 1
|
|
164
|
+
base = type_sym[1]
|
|
165
|
+
values = Parser.coerce_value([:array, base], init_tok)
|
|
166
|
+
arg_pairs << [type_sym, values]
|
|
167
|
+
else
|
|
168
|
+
arg_pairs << [type_sym, nil]
|
|
169
|
+
end
|
|
161
170
|
next
|
|
162
171
|
end
|
|
163
172
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A tiny helper layer inspired by fiddley (BSD-2-Clause) to offer
|
|
4
|
+
# convenient, DSL-friendly utilities on top of Ruby's Fiddle.
|
|
5
|
+
# This is intentionally small and tailored for libcall use-cases.
|
|
6
|
+
|
|
7
|
+
require 'fiddle'
|
|
8
|
+
|
|
9
|
+
module Libcall
|
|
10
|
+
module Fiddley
|
|
11
|
+
# Small helper methods intended for use inside callback blocks
|
|
12
|
+
module DSL
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def ptr(x)
|
|
16
|
+
Fiddle::Pointer.new(Integer(x))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def read(type, p)
|
|
20
|
+
t = type.is_a?(Symbol) ? type : type.to_s
|
|
21
|
+
t_sym = Libcall::TypeMap.lookup(t) || (type.is_a?(Symbol) ? type : nil)
|
|
22
|
+
raise Libcall::Error, "unknown read type: #{type}" unless t_sym
|
|
23
|
+
|
|
24
|
+
pp = p.is_a?(Fiddle::Pointer) ? p : Fiddle::Pointer.new(Integer(p))
|
|
25
|
+
Libcall::TypeMap.read_output_pointer(pp, t_sym)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Typed short-hands
|
|
29
|
+
def char(p) = read(:char, p)
|
|
30
|
+
def uchar(p) = read(:uchar, p)
|
|
31
|
+
def short(p) = read(:short, p)
|
|
32
|
+
def ushort(p) = read(:ushort, p)
|
|
33
|
+
def int(p) = read(:int, p)
|
|
34
|
+
def uint(p) = read(:uint, p)
|
|
35
|
+
def long(p) = read(:long, p)
|
|
36
|
+
def ulong(p) = read(:ulong, p)
|
|
37
|
+
def long_long(p) = read(:long_long, p)
|
|
38
|
+
def ulong_long(p) = read(:ulong_long, p)
|
|
39
|
+
def float(p) = read(:float, p)
|
|
40
|
+
def double(p) = read(:double, p)
|
|
41
|
+
def cstr(p) = read(:string, p)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module Utils
|
|
45
|
+
module_function
|
|
46
|
+
|
|
47
|
+
# Native size_t pack template and size
|
|
48
|
+
SIZET_PACK = (Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG ? 'L!' : 'Q')
|
|
49
|
+
|
|
50
|
+
# Return size in bytes for a given type symbol.
|
|
51
|
+
# Falls back to pointer size for :pointer and :voidp.
|
|
52
|
+
def sizeof(type)
|
|
53
|
+
return Fiddle::SIZEOF_SIZE_T if type == :size_t
|
|
54
|
+
return Fiddle::SIZEOF_VOIDP if %i[pointer voidp].include?(type)
|
|
55
|
+
|
|
56
|
+
# Delegate to Libcall::TypeMap when possible
|
|
57
|
+
Libcall::TypeMap.sizeof(type)
|
|
58
|
+
rescue StandardError
|
|
59
|
+
raise Libcall::Error, "unknown type for sizeof: #{type}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Convert a type symbol to a Fiddle type constant.
|
|
63
|
+
def to_fiddle_type(type)
|
|
64
|
+
return Fiddle::TYPE_SIZE_T if type == :size_t
|
|
65
|
+
return Fiddle::TYPE_VOIDP if %i[pointer voidp].include?(type)
|
|
66
|
+
|
|
67
|
+
Libcall::TypeMap.to_fiddle_type(type)
|
|
68
|
+
rescue StandardError
|
|
69
|
+
raise Libcall::Error, "unknown type for to_fiddle_type: #{type}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Pack template for array values of given base type.
|
|
73
|
+
def array_pack_template(type)
|
|
74
|
+
return SIZET_PACK if type == :size_t
|
|
75
|
+
|
|
76
|
+
# Use TypeMap's packing for standard types
|
|
77
|
+
Libcall::TypeMap.pack_template(type)
|
|
78
|
+
rescue StandardError
|
|
79
|
+
# For generic pointers/addresses, use native unsigned pointer width
|
|
80
|
+
return 'J' if %i[pointer voidp].include?(type)
|
|
81
|
+
|
|
82
|
+
raise Libcall::Error, "Unsupported array base type: #{type}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Convert Ruby array of numbers to a binary string for the given type
|
|
86
|
+
def array2str(type, array)
|
|
87
|
+
array.pack("#{array_pack_template(type)}*")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Convert binary string to Ruby array of the given type
|
|
91
|
+
def str2array(type, str)
|
|
92
|
+
str.unpack("#{array_pack_template(type)}*")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Minimal memory buffer wrapper to make building args/arrays convenient
|
|
97
|
+
class MemoryPointer
|
|
98
|
+
attr_reader :size
|
|
99
|
+
|
|
100
|
+
def initialize(type, count = 1)
|
|
101
|
+
@type = type
|
|
102
|
+
@size = Utils.sizeof(type) * count
|
|
103
|
+
@ptr = Fiddle::Pointer.malloc(@size)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def to_ptr
|
|
107
|
+
@ptr
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def address
|
|
111
|
+
@ptr.to_i
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def write_array(type, values)
|
|
115
|
+
data = Utils.array2str(type, Array(values))
|
|
116
|
+
@ptr[0, data.bytesize] = data
|
|
117
|
+
self
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def read_array(type, count)
|
|
121
|
+
bytes = Utils.sizeof(type) * count
|
|
122
|
+
Utils.str2array(type, @ptr[0, bytes])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def put_bytes(offset, str)
|
|
126
|
+
@ptr[offset, str.bytesize] = str
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def write_bytes(str)
|
|
130
|
+
put_bytes(0, str)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def get_bytes(offset, len)
|
|
134
|
+
@ptr[offset, len]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def read_bytes(len)
|
|
138
|
+
get_bytes(0, len)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Return Fiddle::Pointer stored at this pointer (read void*)
|
|
142
|
+
def read_pointer
|
|
143
|
+
to_ptr.ptr
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Wrap Fiddle::Closure::BlockCaller with friendlier type mapping
|
|
148
|
+
class Function < Fiddle::Closure::BlockCaller
|
|
149
|
+
def initialize(ret, params, &blk)
|
|
150
|
+
r = Utils.to_fiddle_type(ret)
|
|
151
|
+
p = Array(params).map { |t| Utils.to_fiddle_type(t) }
|
|
152
|
+
super(r, p, &blk)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
data/lib/libcall/parser.rb
CHANGED
|
@@ -7,6 +7,9 @@ module Libcall
|
|
|
7
7
|
class Parser
|
|
8
8
|
# Pair-only API helpers
|
|
9
9
|
def self.parse_type(type_str)
|
|
10
|
+
# Callback function pointer: func/callback 'ret(arg,...) { |...| ... }'
|
|
11
|
+
return :callback if %w[func callback].include?(type_str)
|
|
12
|
+
|
|
10
13
|
# Output array spec: out:TYPE[N]
|
|
11
14
|
if type_str.start_with?('out:') && type_str.match(/^out:(.+)\[(\d+)\]$/)
|
|
12
15
|
base = Regexp.last_match(1)
|
|
@@ -51,6 +54,32 @@ module Libcall
|
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
def self.coerce_value(type_sym, token)
|
|
57
|
+
# Callback value: signature + Ruby block
|
|
58
|
+
if type_sym == :callback
|
|
59
|
+
src = strip_quotes(token.to_s)
|
|
60
|
+
m = src.match(/\A\s*([^(\s]+)\s*\(([^)]*)\)\s*(\{.*\})\s*\z/m)
|
|
61
|
+
raise Error, "Invalid callback spec: #{src}" unless m
|
|
62
|
+
|
|
63
|
+
ret_s = m[1].strip
|
|
64
|
+
args_s = m[2].strip
|
|
65
|
+
block_src = m[3]
|
|
66
|
+
|
|
67
|
+
ret_sym = TypeMap.lookup(ret_s)
|
|
68
|
+
raise Error, "Unknown callback return type: #{ret_s}" unless ret_sym
|
|
69
|
+
|
|
70
|
+
arg_syms = if args_s.empty?
|
|
71
|
+
[]
|
|
72
|
+
else
|
|
73
|
+
args_s.split(',').map(&:strip).map do |a|
|
|
74
|
+
sym = TypeMap.lookup(a)
|
|
75
|
+
raise Error, "Unknown callback arg type: #{a}" unless sym
|
|
76
|
+
|
|
77
|
+
sym
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
return { kind: :callback, ret: ret_sym, args: arg_syms, block: block_src }
|
|
82
|
+
end
|
|
54
83
|
# Input array values: comma-separated
|
|
55
84
|
if type_sym.is_a?(Array) && type_sym.first == :array
|
|
56
85
|
base = type_sym[1]
|
data/lib/libcall/type_map.rb
CHANGED
|
@@ -27,6 +27,7 @@ module Libcall
|
|
|
27
27
|
'void' => :void,
|
|
28
28
|
# Common C type names
|
|
29
29
|
'char' => :char,
|
|
30
|
+
'uchar' => :uchar,
|
|
30
31
|
'short' => :short,
|
|
31
32
|
'ushort' => :ushort,
|
|
32
33
|
'int' => :int,
|
|
@@ -35,6 +36,21 @@ module Libcall
|
|
|
35
36
|
'ulong' => :ulong,
|
|
36
37
|
'float' => :float,
|
|
37
38
|
'double' => :double,
|
|
39
|
+
# C-style pointer aliases
|
|
40
|
+
'void*' => :voidp,
|
|
41
|
+
'const void*' => :voidp,
|
|
42
|
+
'const_void*' => :voidp,
|
|
43
|
+
'const_voidp' => :voidp,
|
|
44
|
+
# Underscored variants
|
|
45
|
+
'unsigned_char' => :uchar,
|
|
46
|
+
'unsigned_short' => :ushort,
|
|
47
|
+
'unsigned_int' => :uint,
|
|
48
|
+
'unsigned_long' => :ulong,
|
|
49
|
+
'long_long' => :long_long,
|
|
50
|
+
'unsigned_long_long' => :ulong_long,
|
|
51
|
+
# Short aliases
|
|
52
|
+
'unsigned' => :uint,
|
|
53
|
+
'signed' => :int,
|
|
38
54
|
# Extended type names (stdint-like)
|
|
39
55
|
'int8' => :char,
|
|
40
56
|
'uint8' => :uchar,
|
|
@@ -46,6 +62,15 @@ module Libcall
|
|
|
46
62
|
'uint64' => :ulong_long,
|
|
47
63
|
'float32' => :float,
|
|
48
64
|
'float64' => :double,
|
|
65
|
+
# C99/C11 standard types with _t suffix
|
|
66
|
+
'int8_t' => :char,
|
|
67
|
+
'uint8_t' => :uchar,
|
|
68
|
+
'int16_t' => :short,
|
|
69
|
+
'uint16_t' => :ushort,
|
|
70
|
+
'int32_t' => :int,
|
|
71
|
+
'uint32_t' => :uint,
|
|
72
|
+
'int64_t' => :long_long,
|
|
73
|
+
'uint64_t' => :ulong_long,
|
|
49
74
|
# Size and pointer-sized integers
|
|
50
75
|
'size_t' => :ulong,
|
|
51
76
|
'ssize_t' => :long,
|
|
@@ -96,6 +121,9 @@ module Libcall
|
|
|
96
121
|
return Fiddle::TYPE_VOIDP if %i[out array out_array].include?(tag)
|
|
97
122
|
end
|
|
98
123
|
|
|
124
|
+
# Callback function pointers are passed as void*
|
|
125
|
+
return Fiddle::TYPE_VOIDP if type_sym == :callback
|
|
126
|
+
|
|
99
127
|
case type_sym
|
|
100
128
|
when :void then Fiddle::TYPE_VOID
|
|
101
129
|
when :char then Fiddle::TYPE_CHAR
|
|
@@ -167,6 +195,11 @@ module Libcall
|
|
|
167
195
|
end
|
|
168
196
|
end
|
|
169
197
|
|
|
198
|
+
# Read a single scalar value at address (helper for callbacks)
|
|
199
|
+
def self.read_scalar(ptr, type_sym)
|
|
200
|
+
read_output_pointer(ptr, type_sym)
|
|
201
|
+
end
|
|
202
|
+
|
|
170
203
|
# Allocate memory for an array of base type and count elements
|
|
171
204
|
def self.allocate_array(base_type, count)
|
|
172
205
|
Fiddle::Pointer.malloc(sizeof(base_type) * count)
|
data/lib/libcall/version.rb
CHANGED
data/lib/libcall.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libcall
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kojix2
|
|
@@ -49,6 +49,7 @@ files:
|
|
|
49
49
|
- lib/libcall.rb
|
|
50
50
|
- lib/libcall/caller.rb
|
|
51
51
|
- lib/libcall/cli.rb
|
|
52
|
+
- lib/libcall/fiddley.rb
|
|
52
53
|
- lib/libcall/library_finder.rb
|
|
53
54
|
- lib/libcall/parser.rb
|
|
54
55
|
- lib/libcall/platform.rb
|
|
@@ -72,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
72
73
|
- !ruby/object:Gem::Version
|
|
73
74
|
version: '0'
|
|
74
75
|
requirements: []
|
|
75
|
-
rubygems_version: 3.
|
|
76
|
+
rubygems_version: 3.7.2
|
|
76
77
|
specification_version: 4
|
|
77
78
|
summary: Call functions in shared libraries directly from the CLI
|
|
78
79
|
test_files: []
|