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:
Jeremy Evans 2025-06-28 17:09:24 -07:00
parent 08d4f7893e
commit 2ab38691a2
8 changed files with 322 additions and 3 deletions

View 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

View 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