Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,14 @@ doc/build/
python/xnd/xnd.h
*.egg-info
__pycache__

# Ignore ruby generated files
ruby/Gemfile.lock
*.gem
ruby/tmp/

# tag files
GPATH
GRTAGS
GTAGS

2 changes: 1 addition & 1 deletion python/test_xnd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2319,7 +2319,7 @@ def test_float64(self):
class TestComplexKind(XndTestCase):

def test_complex_kind(self):
self.assertRaises(ValueError, xnd.empty, "Complex")
n self.assertRaises(ValueError, xnd.empty, "Complex")


class TestComplex(XndTestCase):
Expand Down
42 changes: 42 additions & 0 deletions ruby/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Developer notes

## Using C APIs from other Ruby libraries

The xnd Ruby wrapper uses type definitions, macros and functions from the
libndtypes Ruby wrapper. For this purpose, it is important to make sure that
xnd can find the correct headers and shared object files during compile and
linking time.

This requires some modifications in both the ndtypes and xnd repos. ndtypes
must ensure that the relevant header files and shared object are exposed to
other Ruby gems.

## structs in XND

The primary struct that contains data for the XND type is the following:
```
typedef struct XndObject {
VALUE mblock; /* owner of the primary type and memory block */
VALUE type; /* owner of the current type. lives and dies with this obj. */
xnd_t xnd; /* typed view, does not own anything */
} XndObject;
```
As the comments say, the `mblock` is an object of type `MemoryBlockObject` that
is never revealed to the user. It is shared between multiple instances of XND objects
and contains the primary type (i.e the type of the root object).

The `type` attribute is of type `NDT` and exists only on a per-object basis. It is specific
to the particular instance of `XndObject`. Therefore, whenever making a view, it is important
to store a reference to the `mblock` in the GC guard so that the memory that the view needs
to access for its data needs does not get GC'd in case the root object needs to be GC'd.

## Infinite ranges

Ruby 2.6 will introduce infinite ranges using a prettier and less verbose syntax, but for now
we're stuck with using `Float::INFINITY` every time we want to specify an infinite range. This
can quickly get tedious to type. Therefore XND introduces the following syntax for infinite
ranges for references arrays:

* Full range (`0..Float::INFINITY`) : `INF`.
* Part range (`4..Float::INFINITY`) : `4..INF`.

3 changes: 3 additions & 0 deletions ruby/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

Empty file added ruby/History.md
Empty file.
14 changes: 14 additions & 0 deletions ruby/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Introduction

XND is a library for storing typed data defined by ndtypes. It is a wrapper
over the libxnd C library.

# Installation

For direct installation, run: `gem install xnd --pre`.

# Usage

# Acknowledgments

Sponsored by Quansight Inc.
105 changes: 105 additions & 0 deletions ruby/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
require 'rake'
require 'rake/extensiontask'
require 'bundler/gem_tasks'
require 'rake/testtask'
require 'fileutils'
require 'xnd/version.rb'

gemspec = eval(IO.read("xnd.gemspec"))

ext_name = "ruby_xnd"
Rake::ExtensionTask.new(ext_name, gemspec) do |ext|
ext.ext_dir = "ext/#{ext_name}"
ext.source_pattern = "**/*.{c,h}"
end

Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList['test/**/test_*.rb']
end

def run *cmd
sh(cmd.join(" "))
end

task :console do
cmd = ['irb', "-r './lib/xnd.rb'"]
run(*cmd)
end

task :pry do
cmd = ['pry', "-r './lib/xnd.rb'"]
run(*cmd)
end

BASEDIR = Pathname( __FILE__ ).dirname.relative_path_from( Pathname.pwd )
SPECDIR = BASEDIR + 'spec'

VALGRIND_OPTIONS = [
"--tool=memcheck",
#"--leak-check=yes",
"--num-callers=15",
#"--error-limit=no",
"--partial-loads-ok=yes",
"--undef-value-errors=no" #,
#"--dsymutil=yes"
]

CALLGRIND_OPTIONS = [
"--tool=callgrind",
"--dump-instr=yes",
"--simulate-cache=yes",
"--collect-jumps=yes"
]

VALGRIND_MEMORYFILL_OPTIONS = [
"--freelist-vol=100000000",
"--malloc-fill=6D",
"--free-fill=66 ",
]

# partial-loads-ok and undef-value-errors necessary to ignore
# spurious (and eminently ignorable) warnings from the ruby
# interpreter

desc "Run specs under Valgrind."
task :valgrind => [ :compile ] do |task|
cmd = [ 'valgrind' ] + VALGRIND_OPTIONS
cmd += [" rake test "]
run( *cmd )
end

LEAKCHECK_CMD = [ 'ruby', '-Ilib:ext', "#{SPECDIR}/leakcheck.rb" ]

task :clobber do |task|
[
"ext/#{ext_name}/include",
"ext/#{ext_name}/share",
"ext/#{ext_name}/lib",
].each do |f|
puts "deleting folder #{f}..."
FileUtils.rm_rf(f)
end

Dir.chdir("ext/#{ext_name}/xnd/libxnd/") do
system("make clean")
end
end

task :develop do
ext_xnd = "ext/ruby_xnd/xnd"
puts "deleting previously created #{ext_xnd} directory..."
FileUtils.rm_rf(ext_xnd)
Dir.mkdir(ext_xnd)

puts "cloning xnd repo into ext/ folder..."
system("git clone https://github.com/plures/xnd #{ext_xnd}")

Dir.chdir(ext_xnd) do
system("git checkout #{RubyXND::COMMIT}")
end

puts "building gem with rake build..."
system("rake build")
end
2 changes: 2 additions & 0 deletions ruby/ext/ruby_xnd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/include
/xnd
74 changes: 74 additions & 0 deletions ruby/ext/ruby_xnd/extconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require 'mkmf'

def windows?
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
end

def mac?
(/darwin/ =~ RUBY_PLATFORM) != nil
end

def unix?
!windows?
end

# libndtypes config

ndtypes_version = ">= 0.2.0dev5"
ndtypes_spec = Gem::Specification.find_by_name("ndtypes", ndtypes_version)
ndtypes_extdir = File.join(ndtypes_spec.gem_dir, 'ext', 'ruby_ndtypes')
ndtypes_includedir = File.join(ndtypes_extdir, 'include')
ndtypes_libdir = File.join(ndtypes_extdir, 'lib')

find_header("ruby_ndtypes.h", ndtypes_includedir)
raise "cannot find ruby_ndtypes.h in path #{ndtypes_includedir}." unless have_header("ruby_ndtypes.h")

find_header("ndtypes.h", ndtypes_includedir)
find_library("ndtypes", nil, ndtypes_libdir)

dir_config("ndtypes", [ndtypes_includedir], [ndtypes_libdir])

# libxnd config

puts "compiling libxnd for your machine..."
Dir.chdir(File.join(File.dirname(__FILE__) + "/xnd")) do
if unix?
Dir.chdir("libxnd") do
Dir.mkdir(".objs") unless Dir.exists? ".objs"
end

system("./configure --prefix=#{File.expand_path("../")} --with-docs=no " +
"--with-includes=#{ndtypes_includedir}")
system("make")
system("make install")
elsif windows?
raise NotImplementedError, "need to specify build instructions for windows."
end
end

binaries = File.expand_path(File.join(File.dirname(__FILE__) + "/lib/"))
headers = File.expand_path(File.join(File.dirname(__FILE__) + "/include/"))

find_library("xnd", nil, binaries)
find_header("xnd.h", headers)

FileUtils.copy_file File.expand_path(File.join(File.dirname(__FILE__) +
"/ruby_xnd.h")),
"#{headers}/ruby_xnd.h"

dir_config("xnd", [headers], [binaries])

$INSTALLFILES = [
["ruby_xnd.h", "$(archdir)"],
["xnd.h", "$(archdir)"]
]

# for macOS
append_ldflags("-Wl,-rpath #{binaries}")

basenames = %w{util float_pack_unpack gc_guard ruby_xnd}
$objs = basenames.map { |b| "#{b}.o" }
$srcs = basenames.map { |b| "#{b}.c" }

$CFLAGS += " -fPIC -g "
create_makefile("ruby_xnd/ruby_xnd")
Loading