mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 05:29:10 +02:00
Add Set C-API
This should be a minimal C-API needed to deal with Set objects. It supports creating the sets, checking whether an element is the set, adding and removing elements, iterating over the elements, clearing a set, and returning the size of the set. Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
This commit is contained in:
parent
08d4f7893e
commit
2ab38691a2
8 changed files with 322 additions and 3 deletions
|
@ -51,6 +51,7 @@
|
|||
#include "ruby/internal/intern/re.h"
|
||||
#include "ruby/internal/intern/ruby.h"
|
||||
#include "ruby/internal/intern/select.h"
|
||||
#include "ruby/internal/intern/set.h"
|
||||
#include "ruby/internal/intern/signal.h"
|
||||
#include "ruby/internal/intern/sprintf.h"
|
||||
#include "ruby/internal/intern/string.h"
|
||||
|
|
|
@ -93,6 +93,7 @@ RUBY_EXTERN VALUE rb_cRandom; /**< `Random` class. */
|
|||
RUBY_EXTERN VALUE rb_cRange; /**< `Range` class. */
|
||||
RUBY_EXTERN VALUE rb_cRational; /**< `Rational` class. */
|
||||
RUBY_EXTERN VALUE rb_cRegexp; /**< `Regexp` class. */
|
||||
RUBY_EXTERN VALUE rb_cSet; /**< `Set` class. */
|
||||
RUBY_EXTERN VALUE rb_cStat; /**< `File::Stat` class. */
|
||||
RUBY_EXTERN VALUE rb_cString; /**< `String` class. */
|
||||
RUBY_EXTERN VALUE rb_cStruct; /**< `Struct` class. */
|
||||
|
|
111
include/ruby/internal/intern/set.h
Normal file
111
include/ruby/internal/intern/set.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef RBIMPL_INTERN_SET_H /*-*-C++-*-vi:se ft=cpp:*/
|
||||
#define RBIMPL_INTERN_SET_H
|
||||
/**
|
||||
* @file
|
||||
* @author Ruby developers <ruby-core@ruby-lang.org>
|
||||
* @copyright This file is a part of the programming language Ruby.
|
||||
* Permission is hereby granted, to either redistribute and/or
|
||||
* modify this file, provided that the conditions mentioned in the
|
||||
* file COPYING are met. Consult the file for details.
|
||||
* @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
|
||||
* implementation details. Don't take them as canon. They could
|
||||
* rapidly appear then vanish. The name (path) of this header file
|
||||
* is also an implementation detail. Do not expect it to persist
|
||||
* at the place it is now. Developers are free to move it anywhere
|
||||
* anytime at will.
|
||||
* @note To ruby-core: remember that this header can be possibly
|
||||
* recursively included from extension libraries written in C++.
|
||||
* Do not expect for instance `__VA_ARGS__` is always available.
|
||||
* We assume C99 for ruby itself but we don't assume languages of
|
||||
* extension libraries. They could be written in C++98.
|
||||
* @brief Public APIs related to ::rb_cSet.
|
||||
*/
|
||||
#include "ruby/internal/attr/nonnull.h"
|
||||
#include "ruby/internal/dllexport.h"
|
||||
#include "ruby/internal/value.h"
|
||||
|
||||
RBIMPL_SYMBOL_EXPORT_BEGIN()
|
||||
|
||||
/* set.c */
|
||||
|
||||
RBIMPL_ATTR_NONNULL(())
|
||||
/**
|
||||
* Iterates over a set. Calls func with each element of the set and the
|
||||
* argument given. func should return ST_CONTINUE, ST_STOP, or ST_DELETE.
|
||||
*
|
||||
* @param[in] set An instance of ::rb_cSet to iterate over.
|
||||
* @param[in] func Callback function to yield.
|
||||
* @param[in] arg Passed as-is to `func`.
|
||||
* @exception rb_eRuntimeError `set` was tampered during iterating.
|
||||
*/
|
||||
void rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg);
|
||||
|
||||
/**
|
||||
* Creates a new, empty set object.
|
||||
*
|
||||
* @return An allocated new instance of ::rb_cSet.
|
||||
*/
|
||||
VALUE rb_set_new(void);
|
||||
|
||||
/**
|
||||
* Identical to rb_set_new(), except it additionally specifies how many elements
|
||||
* it is expected to contain. This way you can create a set that is large enough
|
||||
* for your need. For large sets, it means it won't need to be reallocated
|
||||
* much, improving performance.
|
||||
*
|
||||
* @param[in] capa Designed capacity of the set.
|
||||
* @return An empty Set, whose capacity is `capa`.
|
||||
*/
|
||||
VALUE rb_set_new_capa(size_t capa);
|
||||
|
||||
/**
|
||||
* Whether the set contains the given element.
|
||||
*
|
||||
* @param[in] set Set to look into.
|
||||
* @param[in] element Set element to look for.
|
||||
* @return true if element is in the set, falst otherwise.
|
||||
*/
|
||||
bool rb_set_lookup(VALUE set, VALUE element);
|
||||
|
||||
/**
|
||||
* Adds element to set.
|
||||
*
|
||||
* @param[in] set Target set table to modify.
|
||||
* @param[in] element Arbitrary Ruby object.
|
||||
* @exception rb_eFrozenError `set` is frozen.
|
||||
* @return true if element was not already in set, false otherwise
|
||||
* @post `element` is in `set`.
|
||||
*/
|
||||
bool rb_set_add(VALUE set, VALUE element);
|
||||
|
||||
/**
|
||||
* Removes all entries from set.
|
||||
*
|
||||
* @param[out] set Target to clear.
|
||||
* @exception rb_eFrozenError `set`is frozen.
|
||||
* @return The passed `set`
|
||||
* @post `set` has no elements.
|
||||
*/
|
||||
VALUE rb_set_clear(VALUE set);
|
||||
|
||||
/**
|
||||
* Removes the element from from set.
|
||||
*
|
||||
* @param[in] set Target set to modify.
|
||||
* @param[in] element Key to delete.
|
||||
* @retval true if element was already in set, false otherwise
|
||||
* @post `set` does not have `element` as an element.
|
||||
*/
|
||||
bool rb_set_delete(VALUE set, VALUE element);
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the set.
|
||||
*
|
||||
* @param[in] set A set object.
|
||||
* @return The size of the set.
|
||||
*/
|
||||
size_t rb_set_size(VALUE set);
|
||||
|
||||
RBIMPL_SYMBOL_EXPORT_END()
|
||||
|
||||
#endif /* RBIMPL_INTERN_SET_H */
|
50
set.c
50
set.c
|
@ -1913,6 +1913,56 @@ compat_loader(VALUE self, VALUE a)
|
|||
return set_i_from_hash(self, rb_ivar_get(a, id_i_hash));
|
||||
}
|
||||
|
||||
/* C-API functions */
|
||||
|
||||
void
|
||||
rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg)
|
||||
{
|
||||
set_iter(set, func, arg);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_set_new(void)
|
||||
{
|
||||
return set_alloc_with_size(rb_cSet, 0);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_set_new_capa(size_t capa)
|
||||
{
|
||||
return set_alloc_with_size(rb_cSet, (st_index_t)capa);
|
||||
}
|
||||
|
||||
bool
|
||||
rb_set_lookup(VALUE set, VALUE element)
|
||||
{
|
||||
return RSET_IS_MEMBER(set, element);
|
||||
}
|
||||
|
||||
bool
|
||||
rb_set_add(VALUE set, VALUE element)
|
||||
{
|
||||
return set_i_add_p(set, element) != Qnil;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_set_clear(VALUE set)
|
||||
{
|
||||
return set_i_clear(set);
|
||||
}
|
||||
|
||||
bool
|
||||
rb_set_delete(VALUE set, VALUE element)
|
||||
{
|
||||
return set_i_delete_p(set, element) != Qnil;
|
||||
}
|
||||
|
||||
size_t
|
||||
rb_set_size(VALUE set)
|
||||
{
|
||||
return RSET_SIZE(set);
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: Set
|
||||
*
|
||||
|
|
62
spec/ruby/optional/capi/ext/set_spec.c
Normal file
62
spec/ruby/optional/capi/ext/set_spec.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "ruby.h"
|
||||
#include "rubyspec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RBOOL(x) ((x) ? Qtrue : Qfalse)
|
||||
|
||||
int yield_element_and_arg(VALUE element, VALUE arg) {
|
||||
return RTEST(rb_yield_values(2, element, arg)) ? ST_CONTINUE : ST_STOP;
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_foreach(VALUE self, VALUE set, VALUE arg) {
|
||||
rb_set_foreach(set, yield_element_and_arg, arg);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_new(VALUE self) {
|
||||
return rb_set_new();
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_new_capa(VALUE self, VALUE capa) {
|
||||
return rb_set_new_capa(NUM2INT(capa));
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_lookup(VALUE self, VALUE set, VALUE element) {
|
||||
return RBOOL(rb_set_lookup(set, element));
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_add(VALUE self, VALUE set, VALUE element) {
|
||||
return RBOOL(rb_set_add(set, element));
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_clear(VALUE self, VALUE set) {
|
||||
return rb_set_clear(set);
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_delete(VALUE self, VALUE set, VALUE element) {
|
||||
return RBOOL(rb_set_delete(set, element));
|
||||
}
|
||||
|
||||
VALUE set_spec_rb_set_size(VALUE self, VALUE set) {
|
||||
return SIZET2NUM(rb_set_size(set));
|
||||
}
|
||||
|
||||
void Init_set_spec(void) {
|
||||
VALUE cls = rb_define_class("CApiSetSpecs", rb_cObject);
|
||||
rb_define_method(cls, "rb_set_foreach", set_spec_rb_set_foreach, 2);
|
||||
rb_define_method(cls, "rb_set_new", set_spec_rb_set_new, 0);
|
||||
rb_define_method(cls, "rb_set_new_capa", set_spec_rb_set_new_capa, 1);
|
||||
rb_define_method(cls, "rb_set_lookup", set_spec_rb_set_lookup, 2);
|
||||
rb_define_method(cls, "rb_set_add", set_spec_rb_set_add, 2);
|
||||
rb_define_method(cls, "rb_set_clear", set_spec_rb_set_clear, 1);
|
||||
rb_define_method(cls, "rb_set_delete", set_spec_rb_set_delete, 2);
|
||||
rb_define_method(cls, "rb_set_size", set_spec_rb_set_size, 1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
96
spec/ruby/optional/capi/set_spec.rb
Normal file
96
spec/ruby/optional/capi/set_spec.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
require_relative 'spec_helper'
|
||||
|
||||
load_extension("set")
|
||||
|
||||
describe "C-API Set function" do
|
||||
before :each do
|
||||
@s = CApiSetSpecs.new
|
||||
end
|
||||
|
||||
ruby_version_is "3.5" do
|
||||
describe "rb_set_foreach" do
|
||||
it "calls function with each element and arg" do
|
||||
a = []
|
||||
@s.rb_set_foreach(Set[1, 2], 3) {|*args| a.concat(args) }
|
||||
a.should == [1, 3, 2, 3]
|
||||
end
|
||||
|
||||
it "respects function return value" do
|
||||
a = []
|
||||
@s.rb_set_foreach(Set[1, 2], 3) do |*args|
|
||||
a.concat(args)
|
||||
false
|
||||
end
|
||||
a.should == [1, 3]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_new" do
|
||||
it "returns a new set" do
|
||||
@s.rb_set_new.should == Set[]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_new_capa" do
|
||||
it "returns a new set" do
|
||||
@s.rb_set_new_capa(3).should == Set[]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_lookup" do
|
||||
it "returns whether the element is in the set" do
|
||||
set = Set[1]
|
||||
@s.rb_set_lookup(set, 1).should == true
|
||||
@s.rb_set_lookup(set, 2).should == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_add" do
|
||||
it "adds element to set" do
|
||||
set = Set[]
|
||||
@s.rb_set_add(set, 1).should == true
|
||||
set.should == Set[1]
|
||||
@s.rb_set_add(set, 2).should == true
|
||||
set.should == Set[1, 2]
|
||||
end
|
||||
|
||||
it "returns false if element is already in set" do
|
||||
set = Set[1]
|
||||
@s.rb_set_add(set, 1).should == false
|
||||
set.should == Set[1]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_clear" do
|
||||
it "empties and returns self" do
|
||||
set = Set[1]
|
||||
@s.rb_set_clear(set).should equal(set)
|
||||
set.should == Set[]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_delete" do
|
||||
it "removes element from set" do
|
||||
set = Set[1, 2]
|
||||
@s.rb_set_delete(set, 1).should == true
|
||||
set.should == Set[2]
|
||||
@s.rb_set_delete(set, 2).should == true
|
||||
set.should == Set[]
|
||||
end
|
||||
|
||||
it "returns false if element is not already in set" do
|
||||
set = Set[2]
|
||||
@s.rb_set_delete(set, 1).should == false
|
||||
set.should == Set[2]
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_set_size" do
|
||||
it "returns number of elements in set" do
|
||||
@s.rb_set_size(Set[]).should == 0
|
||||
@s.rb_set_size(Set[1]).should == 1
|
||||
@s.rb_set_size(Set[1,2]).should == 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
2
zjit.c
2
zjit.c
|
@ -32,8 +32,6 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
RUBY_EXTERN VALUE rb_cSet; // defined in set.c and it's not exposed yet
|
||||
|
||||
uint32_t
|
||||
rb_zjit_get_page_size(void)
|
||||
{
|
||||
|
|
2
zjit/src/cruby_bindings.inc.rs
generated
2
zjit/src/cruby_bindings.inc.rs
generated
|
@ -756,6 +756,7 @@ unsafe extern "C" {
|
|||
pub static mut rb_cNumeric: VALUE;
|
||||
pub static mut rb_cRange: VALUE;
|
||||
pub static mut rb_cRegexp: VALUE;
|
||||
pub static mut rb_cSet: VALUE;
|
||||
pub static mut rb_cString: VALUE;
|
||||
pub static mut rb_cSymbol: VALUE;
|
||||
pub static mut rb_cThread: VALUE;
|
||||
|
@ -905,7 +906,6 @@ unsafe extern "C" {
|
|||
lines: *mut ::std::os::raw::c_int,
|
||||
) -> ::std::os::raw::c_int;
|
||||
pub fn rb_jit_cont_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void);
|
||||
pub static mut rb_cSet: VALUE;
|
||||
pub fn rb_zjit_get_page_size() -> u32;
|
||||
pub fn rb_zjit_reserve_addr_space(mem_size: u32) -> *mut u8;
|
||||
pub fn rb_zjit_profile_disable(iseq: *const rb_iseq_t);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue