ruby/ruby
C extensions
ext/ contains the C extensions that ship with CRuby — bindings to system libraries (sockets, OpenSSL, libyaml), platform features (PTY, fcntl), or performance-critical code (the JSON parser, digest algorithms). They use the mkmf (lib/mkmf.rb) convention: each extension has an extconf.rb that probes for headers/libraries and emits a Makefile, plus C/C++ source that calls the public CRuby API in include/ruby/.
Extension layout
A typical ext/<name>/ directory contains:
ext/<name>/
├── extconf.rb # probes + emits Makefile
├── <name>.c (or many) # the C source
├── <name>.h # internal header
├── depend # generated dependency list (regenerated by `make update-deps`)
└── <name>.gemspec # gem metadata (for default gems)For Ruby-side glue, default-gem extensions also have lib/<name>.rb or lib/<name>/<name>.rb.
Extensions in this repo
The ext/ directory has roughly 40 extensions. Highlights:
Networking and crypto
ext/socket/— Berkeley sockets,Addrinfo,getaddrinfo. ~16,000 lines of C.ext/openssl/— TLS, hashing, public-key crypto, X.509. The biggest single extension.ext/digest/— SHA1/SHA2/MD5/Bcrypt with both pure-Ruby and OpenSSL-backed implementations.
Serialisation
ext/json/— JSON parser and generator, with the high-performance "ext" implementation.ext/psych/— YAML 1.1 (libyaml binding).
IO
ext/io/console/— terminal control, raw mode, getch.ext/io/wait/—IO#wait_readable/IO#wait_writable.ext/io/nonblock/—IO#nonblock=.ext/stringio/—StringIO, an in-memory IO.ext/strscan/—StringScanner, incremental string parsing.
Platform glue
ext/etc/—Etc.passwd,Etc.group,Etc.sysconf.ext/fcntl/—fcntl(2)constants.ext/pty/— pseudo-terminal allocation.ext/syslog/(where built) —syslog(3)interface.ext/zlib/— compression bindings.
Diagnostics
ext/objspace/—ObjectSpace.dump_all,count_objects,memsize_of, allocation tracing.ext/coverage/— code-coverage tool used bysimplecovetc.
Parsers
ext/ripper/— usesparse.yto expose tokens and parse events to Ruby code.ext/prism/— the Ruby gem façade for the Prism parser.
Internals
ext/-test-/— unit tests for the public C API. Built only inmake test-all.ext/-ext-/— additional API tests.ext/fiber/— Fiber pool and scheduler integration.ext/mmtk/— MMTk GC binding (when built with--with-gc=mmtk).ext/date/—DateandDateTime(a substantial C extension).
Anatomy of an extension
ext/digest/sha1/extconf.rb is short:
require_relative "../../digest/extconf"ext/digest/extconf.rb (the parent) does the actual work:
require "mkmf"
$defs << "-DRUBY"
have_header("openssl/sha.h")
have_library("crypto")
create_makefile("digest/sha1")have_header, have_library, have_func, try_compile are mkmf primitives that probe the build environment and append -D flags to $defs. create_makefile emits a Makefile based on the platform's compiler settings.
When make install runs:
- The base build invokes each
ext/<name>/extconf.rbviatool/extmk.rb. - The generated
Makefilebuilds<name>.so(or.bundle/.dll). - The
.sois loaded byrequire "name"viadln_load(see systems/loader.md). - The
Init_<name>function runs, registering classes and methods.
The Init_xxx convention
Every CRuby extension has an entry point named Init_<name> (after the basename of the .so):
#include <ruby.h>
void
Init_mygem(void)
{
VALUE m = rb_define_module("MyGem");
rb_define_singleton_method(m, "hello", mygem_hello, 0);
}dln.c::dln_load calls Init_mygem after dlopen returns. The function may register classes, modules, methods, constants, and global variables.
The public C API
Headers in include/ruby/ are the contract between CRuby and extensions:
| Header | Purpose |
|---|---|
ruby.h |
Convenience top-level header |
ruby/intern.h |
Function declarations (the bulk of the API) |
ruby/internal/... |
Macros, type definitions, low-level details |
ruby/encoding.h |
M17N API |
ruby/io.h |
IO struct + IO API |
ruby/thread.h |
rb_thread_call_without_gvl, rb_thread_check_ints |
ruby/io/buffer.h |
IO::Buffer API |
ruby/onigmo.h |
Embedded regex engine API |
ruby/random.h |
rb_random_t API |
ruby/util.h |
Utility functions (ruby_qsort, ruby_strdup) |
ruby/version.h |
Version macros |
ruby/vm.h |
Limited VM-level helpers |
The headers are documented with Doxygen; the extension.rdoc document under doc/extension.rdoc is the authoritative tutorial for writing one.
Static linking
For builds where dynamic loading isn't available (the WASM port, some embedded scenarios), extensions are statically linked. dmydln.c, dmyenc.c, and dmyext.c ("dummy") provide the static analogues:
dmydln.cprovidesdln_loadthat returns from a registry instead of callingdlopen.dmyenc.cprovides the encoding initialisations statically.dmyext.cprovides the extensionInit_*registry.
make selects between dynamic and static by configure flags.
Building only some extensions
./configure --with-ext=socket,openssl --without-ext=psych--with-ext and --without-ext accept comma-separated lists. Extensions not listed are not built — useful for slimming down builds for embedded scenarios.
CRuby C API stability
The public API in include/ruby/ is mostly stable across minor versions. The maintainers go to lengths to avoid breaking C extensions on minor bumps (e.g., 3.2 → 3.3). Major bumps (e.g., 3.x → 4.0) may include breaking changes; they're listed in NEWS.md.
The internal/ headers under include/ruby/internal/ are not part of the public API even though they're shipped — extensions that include them risk breaking on any release.
How extensions interact with the GC
C extensions must:
- Mark held VALUEs during GC: provide a
dmarkcallback inrb_data_type_t. - Update references during compaction: provide an
update_referencescallback. - Use write barriers on assignment:
RB_OBJ_WRITE/RB_OBJ_WRITTEN. - Free non-VALUE memory in
dfree. - Report memory size via
dsizeforObjectSpace.memsize_of.
TypedData_Make_Struct is the recommended helper for wrapping a C pointer as a Ruby object:
typedef struct { int *buf; size_t len; } my_data_t;
static void my_free(void *p) { my_data_t *d = p; free(d->buf); free(d); }
static size_t my_size(const void *p) { const my_data_t *d = p; return sizeof(*d) + d->len * sizeof(int); }
static const rb_data_type_t my_type = {
"MyData",
{NULL, my_free, my_size},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY,
};
VALUE my_alloc(VALUE klass) {
my_data_t *d;
return TypedData_Make_Struct(klass, my_data_t, &my_type, d);
}Common pitfalls in extension code
- Forgetting to release the GVL during a blocking syscall.
- Not marking VALUEs held in C structs — leads to use-after-free.
- Not calling
rb_check_arity— silent argument mismatch. - Using
RSTRING_PTRafter a Ruby allocation — the underlying buffer might have moved. - Confusing
rb_eval_stringwithrb_funcall— the former evals Ruby source; the latter calls a method on a known receiver.
Entry points for modification
- Add a new method to an extension: edit the relevant C file, add
rb_define_methodinInit_xxx. Re-runmaketo pick up the change. - Probe for a new system feature: edit the extension's
extconf.rbwithhave_func/have_macro/try_compile. - Sync from upstream gem: most C extensions also have upstream gem repos. Use
tool/sync_default_gems.rbto pull updates.
See doc/extension.rdoc for the canonical extension tutorial.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.