direct-bind 0.1.1 → 1.0.0
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/.rspec +1 -0
- data/direct-bind.gemspec +2 -3
- data/{ext/direct_bind_native_extension/direct_bind.c → dist/direct-bind.c} +33 -6
- data/{ext/direct_bind_native_extension/direct_bind.h → dist/direct-bind.h} +17 -2
- data/lib/direct-bind.rb +0 -2
- data/lib/direct_bind/rake.rb +58 -0
- data/{ext/direct_bind_native_extension/extconf.rb → lib/direct_bind/rspec_helper.rb} +11 -18
- data/lib/direct_bind/version.rb +2 -1
- metadata +7 -8
- data/ext/direct_bind_native_extension/direct_bind_native_extension.c +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee80345a659c00cc5dc666e40baf81c76c0c42a08b7bc337ed75087ab0d1514f
|
4
|
+
data.tar.gz: ce718f097ba34a0858f1765bf475eeda66296af4f1fe12f5441ffdf4e1557177
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0357cc24a731f4a3ec624211deaa743009a432e996339bd4bb0d8a2322ee1d4feca27a728f1f08e16a416c50180dbaa869c0a6a53c0661f1c6ff83fc597a3bb1
|
7
|
+
data.tar.gz: 3527f022c312154c455ca4220a694531427dbf08f0bd436c63005f1823e7c60c0bb300d531dd4f71d0e808f52e23e0c71dbf698141ed9038ecb4885f15084a9e
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/direct-bind.gemspec
CHANGED
@@ -42,10 +42,9 @@ Gem::Specification.new do |spec|
|
|
42
42
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
43
43
|
spec.files = Dir.chdir(__dir__) do
|
44
44
|
`git ls-files -z`.split("\x0").reject do |f|
|
45
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|examples)/|\.(?:git|travis|circleci)|appveyor)}) ||
|
45
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|examples|ext)/|\.(?:git|travis|circleci)|appveyor)}) ||
|
46
46
|
[".editorconfig", ".ruby-version", ".standard.yml", "gems.rb", "Rakefile"].include?(f)
|
47
47
|
end
|
48
48
|
end
|
49
|
-
spec.require_paths = ["lib"
|
50
|
-
spec.extensions = ["ext/direct_bind_native_extension/extconf.rb"]
|
49
|
+
spec.require_paths = ["lib"]
|
51
50
|
end
|
@@ -23,7 +23,23 @@
|
|
23
23
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
24
|
// SOFTWARE.
|
25
25
|
|
26
|
-
|
26
|
+
// See direct-bind.h for details on using direct-bind and why you may be finding this file vendored inside another gem.
|
27
|
+
|
28
|
+
#include "direct-bind.h"
|
29
|
+
|
30
|
+
static bool direct_bind_self_test(bool raise_on_failure);
|
31
|
+
|
32
|
+
// # Initialization and version management
|
33
|
+
|
34
|
+
bool direct_bind_initialize(VALUE publish_version_under, bool raise_on_failure) {
|
35
|
+
if (!direct_bind_self_test(raise_on_failure)) return false;
|
36
|
+
|
37
|
+
if (publish_version_under != Qnil) {
|
38
|
+
rb_define_const(rb_define_module_under(publish_version_under, "DirectBind"), "VERSION", rb_str_new_lit(DIRECT_BIND_VERSION));
|
39
|
+
}
|
40
|
+
|
41
|
+
return true;
|
42
|
+
}
|
27
43
|
|
28
44
|
// # Self-test implementation
|
29
45
|
|
@@ -38,16 +54,14 @@ static VALUE self_test_target_func(
|
|
38
54
|
return Qnil;
|
39
55
|
}
|
40
56
|
|
41
|
-
bool direct_bind_self_test(bool raise_on_failure) {
|
57
|
+
static bool direct_bind_self_test(bool raise_on_failure) {
|
42
58
|
VALUE anonymous_module = rb_module_new();
|
43
59
|
rb_define_method(anonymous_module, "direct_bind_self_test_target", self_test_target_func, SELF_TEST_ARITY);
|
44
60
|
|
45
61
|
ID self_test_id = rb_intern("direct_bind_self_test_target");
|
46
|
-
direct_bind_cfunc_result test_target =
|
62
|
+
direct_bind_cfunc_result test_target = direct_bind_get_cfunc_with_arity(anonymous_module, self_test_id, SELF_TEST_ARITY, raise_on_failure);
|
47
63
|
|
48
|
-
|
49
|
-
|
50
|
-
return test_target.arity == SELF_TEST_ARITY && test_target.func == self_test_target_func;
|
64
|
+
return test_target.ok && test_target.func == self_test_target_func;
|
51
65
|
}
|
52
66
|
|
53
67
|
// # Structure layouts and exported symbol definitions from Ruby
|
@@ -101,6 +115,19 @@ direct_bind_cfunc_result direct_bind_get_cfunc(VALUE klass, ID method_name, bool
|
|
101
115
|
return find_data.result;
|
102
116
|
}
|
103
117
|
|
118
|
+
direct_bind_cfunc_result direct_bind_get_cfunc_with_arity(VALUE klass, ID method_name, int arity, bool raise_on_failure) {
|
119
|
+
direct_bind_cfunc_result result = direct_bind_get_cfunc(klass, method_name, raise_on_failure);
|
120
|
+
|
121
|
+
if (result.ok && result.arity != arity) {
|
122
|
+
VALUE unexpected_arity = rb_sprintf("method %"PRIsVALUE".%"PRIsVALUE" unexpected arity %d, expected %d", klass, ID2SYM(method_name), result.arity, arity);
|
123
|
+
|
124
|
+
if (raise_on_failure) rb_raise(rb_eRuntimeError, "direct_bind_get_cfunc_with_arity failed: %"PRIsVALUE, unexpected_arity);
|
125
|
+
else result = (direct_bind_cfunc_result) {.ok = false, .failure_reason = unexpected_arity};
|
126
|
+
}
|
127
|
+
|
128
|
+
return result;
|
129
|
+
}
|
130
|
+
|
104
131
|
// TODO: Maybe change this to use safe memory reads that can never segv (e.g. if structure layouts are off?)
|
105
132
|
static int find_cfunc(void *start, void *end, size_t stride, void *data) {
|
106
133
|
const int stop_iteration = 1;
|
@@ -23,11 +23,22 @@
|
|
23
23
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
24
|
// SOFTWARE.
|
25
25
|
|
26
|
+
// The recommended way to consume the direct-bind gem is to always vendor it.
|
27
|
+
// That is, use its rake task to automatically copy direct-bind.h and direct-bind.c into another gem's native extension
|
28
|
+
// sources folder. (P.s.: There's also a test helper to make sure copying is working fine and the gem is up-to-date.)
|
29
|
+
//
|
30
|
+
// This makes the actual Ruby direct-bind gem only a development dependency, simplifying distribution for the gem
|
31
|
+
// that uses it.
|
32
|
+
//
|
33
|
+
// For more details, check the direct-bind gem's documentation.
|
34
|
+
|
26
35
|
#pragma once
|
27
36
|
|
28
37
|
#include <stdbool.h>
|
29
38
|
#include <ruby.h>
|
30
39
|
|
40
|
+
#define DIRECT_BIND_VERSION "1.0.0"
|
41
|
+
|
31
42
|
typedef struct {
|
32
43
|
bool ok;
|
33
44
|
VALUE failure_reason;
|
@@ -35,8 +46,9 @@ typedef struct {
|
|
35
46
|
VALUE (*func)(ANYARGS);
|
36
47
|
} direct_bind_cfunc_result;
|
37
48
|
|
38
|
-
// Recommended to call once during your gem's initialization, to validate that direct-bind's Ruby hacking is in good shape
|
39
|
-
|
49
|
+
// Recommended to call once during your gem's initialization, to validate that direct-bind's Ruby hacking is in good shape and
|
50
|
+
// to make it easy to (optionally) validate what version you're using
|
51
|
+
bool direct_bind_initialize(VALUE publish_version_under, bool raise_on_failure);
|
40
52
|
|
41
53
|
// Provides the reverse of `rb_define_method`: Given a class and a method_name, retrieves the arity and func previously
|
42
54
|
// passed to `rb_define_method`.
|
@@ -44,3 +56,6 @@ bool direct_bind_self_test(bool raise_on_failure);
|
|
44
56
|
// Performance note: As of this writing, this method scans objspace to find the definition of the method, so you
|
45
57
|
// most probably want to cache its result, rather than calling it very often.
|
46
58
|
direct_bind_cfunc_result direct_bind_get_cfunc(VALUE klass, ID method_name, bool raise_on_failure);
|
59
|
+
|
60
|
+
// Same as above, but automatically fails if arity isn't the expected value
|
61
|
+
direct_bind_cfunc_result direct_bind_get_cfunc_with_arity(VALUE klass, ID method_name, int arity, bool raise_on_failure);
|
data/lib/direct-bind.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
# direct-bind: Ruby gem for getting direct access to function pointers
|
2
|
+
# Copyright (c) 2025 Ivo Anjo <ivo@ivoanjo.me>
|
3
|
+
#
|
4
|
+
# This file is part of direct-bind.
|
5
|
+
#
|
6
|
+
# MIT License
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be included in all
|
16
|
+
# copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
|
26
|
+
# frozen_string_literal: true
|
27
|
+
|
28
|
+
require "rake"
|
29
|
+
require "rake/tasklib"
|
30
|
+
|
31
|
+
module DirectBind
|
32
|
+
module Rake
|
33
|
+
class InstallTask < ::Rake::TaskLib
|
34
|
+
DIRECT_BIND_SOURCES = ["direct-bind.h", "direct-bind.c"]
|
35
|
+
DIRECT_BIND_SOURCES_PATH = File.join(Gem.loaded_specs["direct-bind"].full_gem_path, "dist")
|
36
|
+
|
37
|
+
def initialize(extension_name)
|
38
|
+
target_extension_path = File.join(Dir.pwd, "ext", extension_name)
|
39
|
+
|
40
|
+
desc "Install direct_bind files into extension"
|
41
|
+
task(:"direct-bind:install") do
|
42
|
+
DIRECT_BIND_SOURCES.each do |file_name|
|
43
|
+
from_path = File.join(DIRECT_BIND_SOURCES_PATH, file_name)
|
44
|
+
to_path = File.join(target_extension_path, file_name)
|
45
|
+
|
46
|
+
FileUtils.cp(from_path, to_path) unless already_up_to_date?(from_path, to_path)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def already_up_to_date?(file1, file2) # Order doesn't really matter here
|
54
|
+
File.exist?(file1) && File.exist?(file2) && FileUtils.compare_file(file1, file2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -23,24 +23,17 @@
|
|
23
23
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
24
|
# SOFTWARE.
|
25
25
|
|
26
|
-
|
27
|
-
raise \
|
28
|
-
"\n#{"-" * 80}\nSorry! This gem is unsupported on #{RUBY_ENGINE}. Since it relies on a lot of guts of MRI Ruby, " \
|
29
|
-
"it's impossible to make a direct port.\n" \
|
30
|
-
"Perhaps a #{RUBY_ENGINE} equivalent could be created -- help is welcome! :)\n#{"-" * 80}"
|
31
|
-
end
|
26
|
+
# frozen_string_literal: true
|
32
27
|
|
33
|
-
require "
|
28
|
+
require "direct-bind"
|
29
|
+
require "rspec/expectations"
|
34
30
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
append_cflags("-Werror-implicit-function-declaration")
|
39
|
-
append_cflags("-Wunused-parameter")
|
40
|
-
append_cflags("-Wold-style-definition")
|
41
|
-
append_cflags("-Wall")
|
42
|
-
append_cflags("-Wextra")
|
43
|
-
append_cflags("-Werror") if ENV["ENABLE_WERROR"] == "true"
|
31
|
+
module DirectBind
|
32
|
+
module RSpecHelper
|
33
|
+
self.class.include RSpec::Matchers
|
44
34
|
|
45
|
-
|
46
|
-
|
35
|
+
def self.expect_direct_bind_version_to_be_up_to_date_in(native_extension_module)
|
36
|
+
expect(native_extension_module::DirectBind::VERSION).to eq ::DirectBind::VERSION
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/direct_bind/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: direct-bind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivo Anjo
|
@@ -12,19 +12,19 @@ dependencies: []
|
|
12
12
|
email:
|
13
13
|
- ivo@ivoanjo.me
|
14
14
|
executables: []
|
15
|
-
extensions:
|
16
|
-
- ext/direct_bind_native_extension/extconf.rb
|
15
|
+
extensions: []
|
17
16
|
extra_rdoc_files: []
|
18
17
|
files:
|
18
|
+
- ".rspec"
|
19
19
|
- CODE_OF_CONDUCT.adoc
|
20
20
|
- LICENSE
|
21
21
|
- README.adoc
|
22
22
|
- direct-bind.gemspec
|
23
|
-
-
|
24
|
-
-
|
25
|
-
- ext/direct_bind_native_extension/direct_bind_native_extension.c
|
26
|
-
- ext/direct_bind_native_extension/extconf.rb
|
23
|
+
- dist/direct-bind.c
|
24
|
+
- dist/direct-bind.h
|
27
25
|
- lib/direct-bind.rb
|
26
|
+
- lib/direct_bind/rake.rb
|
27
|
+
- lib/direct_bind/rspec_helper.rb
|
28
28
|
- lib/direct_bind/version.rb
|
29
29
|
homepage: https://github.com/ivoanjo/direct-bind
|
30
30
|
licenses:
|
@@ -33,7 +33,6 @@ metadata: {}
|
|
33
33
|
rdoc_options: []
|
34
34
|
require_paths:
|
35
35
|
- lib
|
36
|
-
- ext
|
37
36
|
required_ruby_version: !ruby/object:Gem::Requirement
|
38
37
|
requirements:
|
39
38
|
- - ">="
|
@@ -1,41 +0,0 @@
|
|
1
|
-
// direct-bind: Ruby gem for getting direct access to function pointers
|
2
|
-
// Copyright (c) 2025 Ivo Anjo <ivo@ivoanjo.me>
|
3
|
-
//
|
4
|
-
// This file is part of direct-bind.
|
5
|
-
//
|
6
|
-
// MIT License
|
7
|
-
//
|
8
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
-
// of this software and associated documentation files (the "Software"), to deal
|
10
|
-
// in the Software without restriction, including without limitation the rights
|
11
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
-
// copies of the Software, and to permit persons to whom the Software is
|
13
|
-
// furnished to do so, subject to the following conditions:
|
14
|
-
//
|
15
|
-
// The above copyright notice and this permission notice shall be included in all
|
16
|
-
// copies or substantial portions of the Software.
|
17
|
-
//
|
18
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
-
// SOFTWARE.
|
25
|
-
|
26
|
-
#include "direct_bind.h"
|
27
|
-
|
28
|
-
VALUE direct_bind_call(VALUE _self, VALUE klass, VALUE method, VALUE instance);
|
29
|
-
|
30
|
-
void Init_direct_bind_native_extension(void) {
|
31
|
-
direct_bind_self_test(true);
|
32
|
-
|
33
|
-
VALUE direct_bind_module = rb_define_module("DirectBind");
|
34
|
-
rb_define_singleton_method(direct_bind_module, "call", direct_bind_call, 3);
|
35
|
-
}
|
36
|
-
|
37
|
-
VALUE direct_bind_call(__attribute__((unused)) VALUE _self, VALUE klass, VALUE method, VALUE instance) {
|
38
|
-
direct_bind_cfunc_result result = direct_bind_get_cfunc(klass, SYM2ID(method), true);
|
39
|
-
if (result.arity != 0) rb_raise(rb_eArgError, "Unexpected arity on cfunc: %d", result.arity);
|
40
|
-
return ((VALUE (*)(VALUE)) result.func)(instance);
|
41
|
-
}
|