rubinius-core-api 0.0.1
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.
- data/README.txt +20 -0
- data/Rakefile +36 -0
- data/lib/rubinius/core-api.rb +16 -0
- data/lib/rubinius/kernel/bootstrap/channel.rb +106 -0
- data/lib/rubinius/kernel/common/bytearray.rb +26 -0
- data/lib/rubinius/kernel/common/channel.rb +21 -0
- data/lib/rubinius/kernel/common/env.rb +239 -0
- data/lib/rubinius/kernel/common/thread.rb +118 -0
- data/lib/rubinius/kernel/common/tuple.rb +148 -0
- data/lib/rubinius/kernel/common/type.rb +114 -0
- data/rubinius-core-api.gemspec +28 -0
- data/src/org/jruby/ext/rubinius/RubiniusByteArray.java +290 -0
- data/src/org/jruby/ext/rubinius/RubiniusChannel.java +115 -0
- data/src/org/jruby/ext/rubinius/RubiniusEnvironmentAccess.java +57 -0
- data/src/org/jruby/ext/rubinius/RubiniusKernel.java +42 -0
- data/src/org/jruby/ext/rubinius/RubiniusLibrary.java +111 -0
- data/src/org/jruby/ext/rubinius/RubiniusTuple.java +302 -0
- data/src/org/jruby/ext/rubinius/RubiniusType.java +43 -0
- metadata +85 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
#--
|
2
|
+
# Be very careful about calling raise in here! Thread has its own
|
3
|
+
# raise which, if you're calling raise, you probably don't want. Use
|
4
|
+
# Kernel.raise to call the proper raise.
|
5
|
+
#++
|
6
|
+
|
7
|
+
class Thread
|
8
|
+
|
9
|
+
attr_reader :recursive_objects
|
10
|
+
|
11
|
+
# Implementation note: ideally, the recursive_objects
|
12
|
+
# lookup table would be different per method call.
|
13
|
+
# Currently it doesn't cause problems, but if ever
|
14
|
+
# a method :foo calls a method :bar which could
|
15
|
+
# recurse back to :foo, it could require making
|
16
|
+
# the tables independant.
|
17
|
+
|
18
|
+
def self.recursion_guard(obj)
|
19
|
+
id = obj.object_id
|
20
|
+
objects = current.recursive_objects
|
21
|
+
objects[id] = true
|
22
|
+
|
23
|
+
begin
|
24
|
+
yield
|
25
|
+
ensure
|
26
|
+
objects.delete id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.guarding?(obj)
|
31
|
+
current.recursive_objects[obj.object_id]
|
32
|
+
end
|
33
|
+
|
34
|
+
# detect_recursion will return if there's a recursion
|
35
|
+
# on obj (or the pair obj+paired_obj).
|
36
|
+
# If there is one, it returns true.
|
37
|
+
# Otherwise, it will yield once and return false.
|
38
|
+
|
39
|
+
def self.detect_recursion(obj, paired_obj=undefined)
|
40
|
+
id = obj.object_id
|
41
|
+
pair_id = paired_obj.object_id
|
42
|
+
objects = current.recursive_objects
|
43
|
+
|
44
|
+
case objects[id]
|
45
|
+
|
46
|
+
# Default case, we haven't seen +obj+ yet, so we add it and run the block.
|
47
|
+
when nil
|
48
|
+
objects[id] = pair_id
|
49
|
+
begin
|
50
|
+
yield
|
51
|
+
ensure
|
52
|
+
objects.delete id
|
53
|
+
end
|
54
|
+
|
55
|
+
# We've seen +obj+ before and it's got multiple paired objects associated
|
56
|
+
# with it, so check the pair and yield if there is no recursion.
|
57
|
+
when Rubinius::LookupTable
|
58
|
+
return true if objects[id][pair_id]
|
59
|
+
objects[id][pair_id] = true
|
60
|
+
|
61
|
+
begin
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
objects[id].delete pair_id
|
65
|
+
end
|
66
|
+
|
67
|
+
# We've seen +obj+ with one paired object, so check the stored one for
|
68
|
+
# recursion.
|
69
|
+
#
|
70
|
+
# This promotes the value to a LookupTable since there is another new paired
|
71
|
+
# object.
|
72
|
+
else
|
73
|
+
previous = objects[id]
|
74
|
+
return true if previous == pair_id
|
75
|
+
|
76
|
+
objects[id] = Rubinius::LookupTable.new(previous => true, pair_id => true)
|
77
|
+
|
78
|
+
begin
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
objects[id] = previous
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
88
|
+
# Similar to detect_recursion, but will short circuit all inner recursion
|
89
|
+
# levels (using a throw)
|
90
|
+
|
91
|
+
class InnerRecursionDetected < Exception; end
|
92
|
+
|
93
|
+
def self.detect_outermost_recursion(obj, paired_obj=undefined, &block)
|
94
|
+
rec = current.recursive_objects
|
95
|
+
|
96
|
+
if rec[:__detect_outermost_recursion__]
|
97
|
+
if detect_recursion(obj, paired_obj, &block)
|
98
|
+
raise InnerRecursionDetected.new
|
99
|
+
end
|
100
|
+
false
|
101
|
+
else
|
102
|
+
begin
|
103
|
+
rec[:__detect_outermost_recursion__] = true
|
104
|
+
|
105
|
+
begin
|
106
|
+
detect_recursion(obj, paired_obj, &block)
|
107
|
+
rescue InnerRecursionDetected
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
|
111
|
+
return nil
|
112
|
+
ensure
|
113
|
+
rec.delete :__detect_outermost_recursion__
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
##
|
2
|
+
# The tuple data type.
|
3
|
+
# A simple storage class. Created to contain a fixed number of elements.
|
4
|
+
#
|
5
|
+
# Not designed to be subclassed, as it does not call initialize
|
6
|
+
# on new instances.
|
7
|
+
|
8
|
+
module Rubinius
|
9
|
+
class Tuple
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def self.[](*args)
|
14
|
+
start = args.start
|
15
|
+
tot = args.size
|
16
|
+
return new(tot).copy_from(args.tuple, start, tot, 0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} #{fields} elements>"
|
21
|
+
end
|
22
|
+
|
23
|
+
def each
|
24
|
+
i = 0
|
25
|
+
t = fields
|
26
|
+
while i < t
|
27
|
+
yield at(i)
|
28
|
+
i += 1
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(tup)
|
34
|
+
return super unless tup.kind_of?(Tuple)
|
35
|
+
|
36
|
+
t = fields()
|
37
|
+
|
38
|
+
return false unless t == tup.size
|
39
|
+
|
40
|
+
i = 0
|
41
|
+
while i < t
|
42
|
+
return false unless at(i) == tup.at(i)
|
43
|
+
i += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
def +(o)
|
50
|
+
t = Tuple.new(size + o.size)
|
51
|
+
t.copy_from(self,0,size,0)
|
52
|
+
t.copy_from(o,0,o.size,size)
|
53
|
+
t
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
str = "#<#{self.class}"
|
58
|
+
if fields == 0
|
59
|
+
str << " empty>"
|
60
|
+
else
|
61
|
+
str << ": #{join(", ", :inspect)}>"
|
62
|
+
end
|
63
|
+
return str
|
64
|
+
end
|
65
|
+
|
66
|
+
def join(sep, meth=:to_s)
|
67
|
+
join_upto(sep, fields, meth)
|
68
|
+
end
|
69
|
+
|
70
|
+
def join_upto(sep, count, meth=:to_s)
|
71
|
+
str = ""
|
72
|
+
return str if count == 0 or empty?
|
73
|
+
|
74
|
+
count = fields if count >= fields
|
75
|
+
count -= 1
|
76
|
+
i = 0
|
77
|
+
while i < count
|
78
|
+
str.append at(i).__send__(meth)
|
79
|
+
str.append sep.dup
|
80
|
+
i += 1
|
81
|
+
end
|
82
|
+
|
83
|
+
str.append at(count).__send__(meth)
|
84
|
+
return str
|
85
|
+
end
|
86
|
+
|
87
|
+
def ===(other)
|
88
|
+
return false unless Tuple === other and fields == other.fields
|
89
|
+
i = 0
|
90
|
+
while i < fields
|
91
|
+
return false unless at(i) === other.at(i)
|
92
|
+
i += 1
|
93
|
+
end
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_a
|
98
|
+
ary = []
|
99
|
+
ary.tuple = dup
|
100
|
+
ary.total = fields
|
101
|
+
ary.start = 0
|
102
|
+
return ary
|
103
|
+
end
|
104
|
+
|
105
|
+
def shift
|
106
|
+
return self unless fields > 0
|
107
|
+
t = Tuple.new(fields-1)
|
108
|
+
t.copy_from self, 1, fields-1, 0
|
109
|
+
return t
|
110
|
+
end
|
111
|
+
|
112
|
+
# Swap elements of the two indexes.
|
113
|
+
def swap(a, b)
|
114
|
+
temp = at(a)
|
115
|
+
put a, at(b)
|
116
|
+
put b, temp
|
117
|
+
end
|
118
|
+
|
119
|
+
alias_method :size, :fields
|
120
|
+
alias_method :length, :fields
|
121
|
+
|
122
|
+
def empty?
|
123
|
+
size == 0
|
124
|
+
end
|
125
|
+
|
126
|
+
def first
|
127
|
+
at(0)
|
128
|
+
end
|
129
|
+
|
130
|
+
def last
|
131
|
+
at(fields - 1)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Marshal support - _dump / _load are deprecated so eventually we should figure
|
135
|
+
# out a better way.
|
136
|
+
def _dump(depth)
|
137
|
+
# TODO use depth
|
138
|
+
Marshal.dump to_a
|
139
|
+
end
|
140
|
+
|
141
|
+
def self._load(str)
|
142
|
+
ary = Marshal.load(str)
|
143
|
+
t = new(ary.size)
|
144
|
+
ary.each_with_index { |obj, idx| t[idx] = obj }
|
145
|
+
return t
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
##
|
2
|
+
# Namespace for coercion functions between various ruby objects.
|
3
|
+
|
4
|
+
module Rubinius
|
5
|
+
module Type
|
6
|
+
|
7
|
+
##
|
8
|
+
# Returns an object of given class. If given object already is one, it is
|
9
|
+
# returned. Otherwise tries obj.meth and returns the result if it is of the
|
10
|
+
# right kind. TypeErrors are raised if the conversion method fails or the
|
11
|
+
# conversion result is wrong.
|
12
|
+
#
|
13
|
+
# Uses Rubinius::Type.object_kind_of to bypass type check overrides.
|
14
|
+
#
|
15
|
+
# Equivalent to MRI's rb_convert_type().
|
16
|
+
|
17
|
+
def self.coerce_to(obj, cls, meth)
|
18
|
+
return obj if object_kind_of?(obj, cls)
|
19
|
+
|
20
|
+
begin
|
21
|
+
ret = obj.__send__(meth)
|
22
|
+
rescue Exception => orig
|
23
|
+
raise TypeError,
|
24
|
+
"Coercion error: #{obj.inspect}.#{meth} => #{cls} failed",
|
25
|
+
orig
|
26
|
+
end
|
27
|
+
|
28
|
+
return ret if object_kind_of?(ret, cls)
|
29
|
+
|
30
|
+
msg = "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{object_class(ret)})"
|
31
|
+
raise TypeError, msg
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Same as coerce_to but returns nil if conversion fails.
|
36
|
+
# Corresponds to MRI's rb_check_convert_type()
|
37
|
+
#
|
38
|
+
def self.try_convert(obj, cls, meth)
|
39
|
+
return obj if object_kind_of?(obj, cls)
|
40
|
+
return nil unless obj.respond_to?(meth)
|
41
|
+
|
42
|
+
begin
|
43
|
+
ret = obj.__send__(meth)
|
44
|
+
rescue Exception
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
return ret if ret.nil? || object_kind_of?(ret, cls)
|
49
|
+
|
50
|
+
msg = "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{object_class(ret)})"
|
51
|
+
raise TypeError, msg
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.coerce_to_symbol(obj)
|
55
|
+
if object_kind_of?(obj, Fixnum)
|
56
|
+
raise ArgumentError, "Fixnums (#{obj}) cannot be used as symbols"
|
57
|
+
end
|
58
|
+
obj = obj.to_str if obj.respond_to?(:to_str)
|
59
|
+
|
60
|
+
coerce_to(obj, Symbol, :to_sym)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.coerce_to_comparison(a, b)
|
64
|
+
unless cmp = (a <=> b)
|
65
|
+
raise ArgumentError, "comparison of #{a.inspect} with #{b.inspect} failed"
|
66
|
+
end
|
67
|
+
cmp
|
68
|
+
end
|
69
|
+
|
70
|
+
# Maps to rb_num2long in MRI
|
71
|
+
def self.num2long(obj)
|
72
|
+
if obj == nil
|
73
|
+
raise TypeError, "no implicit conversion from nil to integer"
|
74
|
+
else
|
75
|
+
Integer(obj)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.each_ancestor(mod)
|
80
|
+
unless object_kind_of?(mod, Class) and singleton_class_object(mod)
|
81
|
+
yield mod
|
82
|
+
end
|
83
|
+
|
84
|
+
sup = mod.direct_superclass()
|
85
|
+
while sup
|
86
|
+
if object_kind_of?(sup, IncludedModule)
|
87
|
+
yield sup.module
|
88
|
+
elsif object_kind_of?(sup, Class)
|
89
|
+
yield sup unless singleton_class_object(sup)
|
90
|
+
else
|
91
|
+
yield sup
|
92
|
+
end
|
93
|
+
sup = sup.direct_superclass()
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.ivar_validate(name)
|
98
|
+
# adapted from rb_to_id
|
99
|
+
case name
|
100
|
+
when String
|
101
|
+
return name.to_sym if name[0] == ?@
|
102
|
+
when Symbol
|
103
|
+
return name if name.is_ivar?
|
104
|
+
when Fixnum
|
105
|
+
raise ArgumentError, "#{name.inspect} is not a symbol"
|
106
|
+
else
|
107
|
+
name = Rubinius::Type.coerce_to(name, String, :to_str)
|
108
|
+
return name.to_sym if name[0] == ?@
|
109
|
+
end
|
110
|
+
|
111
|
+
raise NameError, "`#{name}' is not allowed as an instance variable name"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
base_specification = proc do |s|
|
4
|
+
s.name = %q{rubinius-core-api}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
s.authors = ["Charles Oliver Nutter"]
|
7
|
+
s.date = Time.now
|
8
|
+
s.description = "Cross-impl versions of interesting Rubinius core classes"
|
9
|
+
s.email = ["headius@headius.com"]
|
10
|
+
s.files = Dir['{lib,src}/**/*'] + Dir['{*.txt,*.gemspec,Rakefile}']
|
11
|
+
s.homepage = "http://github.com/rubinius/rubinius-core-api"
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.summary = "Cross-impl versions of interesting Rubinius core classes"
|
14
|
+
s.test_files = Dir["test/test*.rb"]
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined?(JRUBY_VERSION)
|
18
|
+
Gem::Specification.new do |s|
|
19
|
+
s.tap(&base_specification)
|
20
|
+
s.platform = "java"
|
21
|
+
end
|
22
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
23
|
+
Gem::Specification.new do |s|
|
24
|
+
s.tap(&base_specification)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
raise "Only supported on JRuby right now."
|
28
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
/*
|
2
|
+
**** BEGIN LICENSE BLOCK *****
|
3
|
+
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
4
|
+
*
|
5
|
+
* The contents of this file are subject to the Common Public
|
6
|
+
* License Version 1.0 (the "License"); you may not use this file
|
7
|
+
* except in compliance with the License. You may obtain a copy of
|
8
|
+
* the License at http://www.eclipse.org/legal/cpl-v10.html
|
9
|
+
*
|
10
|
+
* Software distributed under the License is distributed on an "AS
|
11
|
+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
12
|
+
* implied. See the License for the specific language governing
|
13
|
+
* rights and limitations under the License.
|
14
|
+
*
|
15
|
+
* Copyright (C) 2011 Charles O Nutter <headius@headius.com>
|
16
|
+
*
|
17
|
+
* Alternatively, the contents of this file may be used under the terms of
|
18
|
+
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
19
|
+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
20
|
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
21
|
+
* of those above. If you wish to allow use of your version of this file only
|
22
|
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
23
|
+
* use your version of this file under the terms of the CPL, indicate your
|
24
|
+
* decision by deleting the provisions above and replace them with the notice
|
25
|
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
26
|
+
* the provisions above, a recipient may use your version of this file under
|
27
|
+
* the terms of any one of the CPL, the GPL or the LGPL.
|
28
|
+
***** END LICENSE BLOCK *****/
|
29
|
+
package org.jruby.ext.rubinius;
|
30
|
+
|
31
|
+
import org.jruby.Ruby;
|
32
|
+
import org.jruby.RubyClass;
|
33
|
+
import org.jruby.RubyFixnum;
|
34
|
+
import org.jruby.RubyObject;
|
35
|
+
import org.jruby.RubyString;
|
36
|
+
import org.jruby.anno.JRubyMethod;
|
37
|
+
import org.jruby.runtime.ObjectAllocator;
|
38
|
+
import org.jruby.runtime.ThreadContext;
|
39
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
40
|
+
import org.jruby.util.ByteList;
|
41
|
+
|
42
|
+
public class RubiniusByteArray extends RubyObject {
|
43
|
+
|
44
|
+
private final byte[] bytes;
|
45
|
+
|
46
|
+
RubiniusByteArray(Ruby runtime, int size) {
|
47
|
+
// FIXME: need to store metaclass somewhere...
|
48
|
+
super(runtime, (RubyClass) runtime.getClassFromPath("Rubinius::ByteArray"));
|
49
|
+
|
50
|
+
bytes = new byte[size];
|
51
|
+
}
|
52
|
+
|
53
|
+
RubiniusByteArray(Ruby runtime, byte[] bytes) {
|
54
|
+
// FIXME: need to store metaclass somewhere...
|
55
|
+
super(runtime, (RubyClass) runtime.getClassFromPath("Rubinius::ByteArray"));
|
56
|
+
|
57
|
+
this.bytes = bytes;
|
58
|
+
}
|
59
|
+
|
60
|
+
public static void createByteArrayClass(Ruby runtime) {
|
61
|
+
RubyClass baClass = runtime.getOrCreateModule("Rubinius").defineClassUnder("ByteArray", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
|
62
|
+
baClass.setReifiedClass(RubiniusByteArray.class);
|
63
|
+
|
64
|
+
baClass.defineAnnotatedMethods(RubiniusByteArray.class);
|
65
|
+
|
66
|
+
// add "data" method to String for spec purposes; not recommended for use (raw byte[] access)
|
67
|
+
runtime.getString().defineAnnotatedMethods(RubiniusString.class);
|
68
|
+
}
|
69
|
+
|
70
|
+
static RubiniusByteArray create(ThreadContext context, int size) {
|
71
|
+
return new RubiniusByteArray(context.runtime, size);
|
72
|
+
}
|
73
|
+
|
74
|
+
private int size() {
|
75
|
+
return bytes.length;
|
76
|
+
}
|
77
|
+
|
78
|
+
@JRubyMethod(meta = true)
|
79
|
+
public static IRubyObject allocate(ThreadContext state, IRubyObject baClass, IRubyObject size) {
|
80
|
+
throw state.runtime.newTypeError("ByteArray cannot be created via allocate()");
|
81
|
+
}
|
82
|
+
|
83
|
+
@JRubyMethod(name = {"new", "allocate_sized"}, meta = true)
|
84
|
+
public static IRubyObject allocate_sized(ThreadContext state, IRubyObject baClass, IRubyObject bytes) {
|
85
|
+
long size = bytes.convertToInteger().getLongValue();
|
86
|
+
if(size < 0) {
|
87
|
+
RubiniusLibrary.argument_error(state, "negative byte array size");
|
88
|
+
} else if (size > Integer.MAX_VALUE) {
|
89
|
+
RubiniusLibrary.argument_error(state, "too large byte array size");
|
90
|
+
}
|
91
|
+
return RubiniusByteArray.create(state, (int)size);
|
92
|
+
}
|
93
|
+
|
94
|
+
@JRubyMethod
|
95
|
+
public IRubyObject fetch_bytes(ThreadContext state, IRubyObject start, IRubyObject count) {
|
96
|
+
long src = start.convertToInteger().getLongValue();
|
97
|
+
long cnt = count.convertToInteger().getLongValue();
|
98
|
+
|
99
|
+
if (src < 0) {
|
100
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "start less than zero");
|
101
|
+
} else if (cnt < 0) {
|
102
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "count less than zero");
|
103
|
+
} else if ((src + cnt) > size()) {
|
104
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "fetch is more than available bytes");
|
105
|
+
}
|
106
|
+
|
107
|
+
RubiniusByteArray ba = RubiniusByteArray.create(state, (int) cnt + 1);
|
108
|
+
System.arraycopy(this.bytes, (int) src, ba.bytes, 0, (int) cnt);
|
109
|
+
ba.bytes[(int) cnt] = 0;
|
110
|
+
|
111
|
+
return ba;
|
112
|
+
}
|
113
|
+
|
114
|
+
@JRubyMethod
|
115
|
+
public IRubyObject move_bytes(ThreadContext state, IRubyObject start, IRubyObject count, IRubyObject dest) {
|
116
|
+
long src = start.convertToInteger().getLongValue();
|
117
|
+
long cnt = count.convertToInteger().getLongValue();
|
118
|
+
long dst = dest.convertToInteger().getLongValue();
|
119
|
+
|
120
|
+
if (src < 0) {
|
121
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "start less than zero");
|
122
|
+
} else if (dst < 0) {
|
123
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "dest less than zero");
|
124
|
+
} else if (cnt < 0) {
|
125
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "count less than zero");
|
126
|
+
} else if ((dst + cnt) > size()) {
|
127
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "move is beyond end of bytearray");
|
128
|
+
} else if ((src + cnt) > size()) {
|
129
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "move is more than available bytes");
|
130
|
+
}
|
131
|
+
|
132
|
+
System.arraycopy(this.bytes, (int) src, this.bytes, (int) dst, (int) cnt);
|
133
|
+
|
134
|
+
return count;
|
135
|
+
}
|
136
|
+
|
137
|
+
@JRubyMethod
|
138
|
+
public IRubyObject get_byte(ThreadContext state, IRubyObject index) {
|
139
|
+
long idx = index.convertToInteger().getLongValue();
|
140
|
+
|
141
|
+
if (idx < 0 || idx >= size()) {
|
142
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "index out of bounds");
|
143
|
+
}
|
144
|
+
|
145
|
+
return RubyFixnum.newFixnum(state.runtime, this.bytes[(int) idx]);
|
146
|
+
}
|
147
|
+
|
148
|
+
@JRubyMethod
|
149
|
+
public IRubyObject set_byte(ThreadContext state, IRubyObject index, IRubyObject value) {
|
150
|
+
long idx = index.convertToInteger().getLongValue();
|
151
|
+
|
152
|
+
if (idx < 0 || idx >= size()) {
|
153
|
+
RubiniusLibrary.object_bounds_exceeded_error(state, "index out of bounds");
|
154
|
+
}
|
155
|
+
|
156
|
+
this.bytes[(int) idx] = (byte) value.convertToInteger().getLongValue();
|
157
|
+
return RubyFixnum.newFixnum(state.runtime, this.bytes[(int) idx]);
|
158
|
+
}
|
159
|
+
|
160
|
+
@JRubyMethod
|
161
|
+
public IRubyObject compare_bytes(ThreadContext state, IRubyObject _other, IRubyObject a, IRubyObject b) {
|
162
|
+
RubiniusByteArray other = (RubiniusByteArray) _other;
|
163
|
+
long slim = a.convertToInteger().getLongValue();
|
164
|
+
long olim = b.convertToInteger().getLongValue();
|
165
|
+
|
166
|
+
if (slim < 0) {
|
167
|
+
RubiniusLibrary.object_bounds_exceeded_error(state,
|
168
|
+
"bytes of self to compare is less than zero");
|
169
|
+
} else if (olim < 0) {
|
170
|
+
RubiniusLibrary.object_bounds_exceeded_error(state,
|
171
|
+
"bytes of other to compare is less than zero");
|
172
|
+
}
|
173
|
+
|
174
|
+
// clamp limits to actual sizes
|
175
|
+
long m = size() < slim ? size() : slim;
|
176
|
+
long n = other.size() < olim ? other.size() : olim;
|
177
|
+
|
178
|
+
// only compare the shortest string
|
179
|
+
long len = m < n ? m : n;
|
180
|
+
|
181
|
+
long cmp = ByteList.memcmp(this.bytes, 0, other.bytes, 0, (int) len);
|
182
|
+
|
183
|
+
// even if substrings are equal, check actual requested limits
|
184
|
+
// of comparison e.g. "xyz", "xyzZ"
|
185
|
+
if (cmp == 0) {
|
186
|
+
if (m < n) {
|
187
|
+
return RubyFixnum.minus_one(state.runtime);
|
188
|
+
} else if (m > n) {
|
189
|
+
return RubyFixnum.one(state.runtime);
|
190
|
+
} else {
|
191
|
+
return RubyFixnum.zero(state.runtime);
|
192
|
+
}
|
193
|
+
} else {
|
194
|
+
return cmp < 0 ? RubyFixnum.minus_one(state.runtime) : RubyFixnum.one(state.runtime);
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
@JRubyMethod
|
199
|
+
public IRubyObject size(ThreadContext state) {
|
200
|
+
return RubyFixnum.newFixnum(state.runtime, bytes.length);
|
201
|
+
}
|
202
|
+
|
203
|
+
@JRubyMethod
|
204
|
+
public IRubyObject dup() {
|
205
|
+
return super.dup();
|
206
|
+
}
|
207
|
+
|
208
|
+
@JRubyMethod
|
209
|
+
public IRubyObject locate(ThreadContext state, IRubyObject pattern, IRubyObject start, IRubyObject max_o) {
|
210
|
+
byte[] pat = pattern.convertToString().getByteList().bytes();
|
211
|
+
int len = pat.length;
|
212
|
+
long max = max_o.convertToInteger().getLongValue();
|
213
|
+
|
214
|
+
if (len == 0) {
|
215
|
+
return start;
|
216
|
+
}
|
217
|
+
|
218
|
+
if (max == 0 || max > size()) {
|
219
|
+
max = size();
|
220
|
+
}
|
221
|
+
|
222
|
+
max -= (len - 1);
|
223
|
+
|
224
|
+
for (int i = (int) start.convertToInteger().getLongValue(); i < max; i++) {
|
225
|
+
if (this.bytes[i] == pat[0]) {
|
226
|
+
int j;
|
227
|
+
// match the rest of the pattern string
|
228
|
+
for (j = 1; j < len; j++) {
|
229
|
+
if (this.bytes[i + j] != pat[j]) {
|
230
|
+
break;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
// if the full pattern matched, return the index
|
235
|
+
// of the end of the pattern in 'this'.
|
236
|
+
if (j == len) {
|
237
|
+
return RubyFixnum.newFixnum(state.runtime, i + len);
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
return state.nil;
|
243
|
+
}
|
244
|
+
|
245
|
+
@JRubyMethod
|
246
|
+
public IRubyObject prepend(ThreadContext state, IRubyObject _str) {
|
247
|
+
RubyString str = _str.convertToString();
|
248
|
+
RubiniusByteArray ba = RubiniusByteArray.create(state, size() + str.size());
|
249
|
+
|
250
|
+
System.arraycopy(str.getByteList().unsafeBytes(), str.getByteList().getBegin(), ba.bytes, 0, str.size());
|
251
|
+
System.arraycopy(bytes, 0, ba.bytes, str.size(), size());
|
252
|
+
|
253
|
+
return ba;
|
254
|
+
}
|
255
|
+
|
256
|
+
@JRubyMethod
|
257
|
+
public IRubyObject utf8_char(ThreadContext state, IRubyObject offset) {
|
258
|
+
// not sure what this is supposed to return or best way to get it in JRuby yet
|
259
|
+
throw state.runtime.newNotImplementedError("ByteArray#utf8_char not implemented");
|
260
|
+
}
|
261
|
+
|
262
|
+
@JRubyMethod
|
263
|
+
public IRubyObject reverse(ThreadContext state, IRubyObject o_start, IRubyObject o_total) {
|
264
|
+
long start = o_start.convertToInteger().getLongValue();
|
265
|
+
long total = o_total.convertToInteger().getLongValue();
|
266
|
+
|
267
|
+
if (total <= 0 || start < 0 || start >= size()) {
|
268
|
+
return this;
|
269
|
+
}
|
270
|
+
|
271
|
+
int pos1 = (int) start;
|
272
|
+
int pos2 = (int) total - 1;
|
273
|
+
byte tmp;
|
274
|
+
|
275
|
+
while (pos1 < pos2) {
|
276
|
+
tmp = bytes[pos1];
|
277
|
+
bytes[pos1++] = bytes[pos2];
|
278
|
+
bytes[pos2--] = tmp;
|
279
|
+
}
|
280
|
+
|
281
|
+
return this;
|
282
|
+
}
|
283
|
+
|
284
|
+
public static class RubiniusString {
|
285
|
+
@JRubyMethod
|
286
|
+
public static IRubyObject data(ThreadContext context, IRubyObject string) {
|
287
|
+
return new RubiniusByteArray(context.runtime, ((RubyString)string).getByteList().getUnsafeBytes());
|
288
|
+
}
|
289
|
+
}
|
290
|
+
}
|