type_array 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.rdoc +4 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +18 -0
- data/README.rdoc +140 -0
- data/Rakefile +47 -0
- data/ext/type_array/array_buffer.c +183 -0
- data/ext/type_array/array_buffer.h +19 -0
- data/ext/type_array/data_view.c +574 -0
- data/ext/type_array/data_view.h +35 -0
- data/ext/type_array/extconf.rb +14 -0
- data/ext/type_array/jruby.h +9 -0
- data/ext/type_array/prelude.h +25 -0
- data/ext/type_array/rubinius.h +19 -0
- data/ext/type_array/ruby18.h +15 -0
- data/ext/type_array/ruby19.h +12 -0
- data/ext/type_array/type_array.c +540 -0
- data/ext/type_array/type_array.h +38 -0
- data/ext/type_array/type_array_ext.c +25 -0
- data/ext/type_array/type_array_ext.h +71 -0
- data/lib/type_array/io.rb +28 -0
- data/lib/type_array/version.rb +3 -0
- data/lib/type_array.rb +14 -0
- data/test/helper.rb +23 -0
- data/test/test_array_buffer.rb +132 -0
- data/test/test_data_view.rb +218 -0
- data/test/test_float_32_array.rb +36 -0
- data/test/test_float_64_array.rb +38 -0
- data/test/test_int_16_array.rb +37 -0
- data/test/test_int_32_array.rb +43 -0
- data/test/test_int_8_array.rb +29 -0
- data/test/test_type_array.rb +155 -0
- data/test/test_uint_16_array.rb +35 -0
- data/test/test_uint_32_array.rb +35 -0
- data/test/test_uint_8_array.rb +33 -0
- data/type_array.gemspec +22 -0
- metadata +112 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- rbx-18mode
|
4
|
+
- rbx-19mode
|
5
|
+
- ree
|
6
|
+
- 1.8.7
|
7
|
+
- 1.9.2
|
8
|
+
- 1.9.3
|
9
|
+
- ruby-head
|
10
|
+
script: "bundle exec rake"
|
11
|
+
gemfile:
|
12
|
+
- Gemfile
|
13
|
+
notifications:
|
14
|
+
recipients:
|
15
|
+
- lourens@methodmissing.com
|
16
|
+
branches:
|
17
|
+
only:
|
18
|
+
- master
|
data/CHANGELOG.rdoc
ADDED
data/Gemfile.lock
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
= TypeArray - Ruby implementation of the ECMAScript spec ( http://wiki.ecmascript.org/doku.php?id=strawman:typed_arrays ) {<img src="https://secure.travis-ci.org/methodmissing/type_array.png" alt="Build Status" />}[http://travis-ci.org/methodmissing/type_array]
|
2
|
+
|
3
|
+
(c) 2012 Lourens Naudé (methodmissing)
|
4
|
+
|
5
|
+
http://github.com/methodmissing/type_array
|
6
|
+
|
7
|
+
This library provides portable, high performance and memory-safe access to native-typed binary data. It defines a generic fixed length buffer type as well as accessor types (views) for accessing data stored within the buffer.
|
8
|
+
|
9
|
+
Where binary data has needed to be manipulated, it is often stored as a String or Array. Both of these methods are slow and error-prone. Several protocol implementations would benefit from being able to read and write binary data directly to it's native form.
|
10
|
+
|
11
|
+
Supported numeric types :
|
12
|
+
|
13
|
+
* an unsigned-integer: one of uint8, uint16, uint32
|
14
|
+
* a signed-integer: one of int8, int16, int32
|
15
|
+
* a floating-point: one of float32 or float64
|
16
|
+
|
17
|
+
There's a few different kinds of typed arrays defined :
|
18
|
+
|
19
|
+
* Int8Array - 8-bit 2's complement signed integer
|
20
|
+
* UInt8Array - 8-bit unsigned integer
|
21
|
+
* Int16Array - 16-bit 2's complement signed integer
|
22
|
+
* UInt16Array - 16-bit unsigned integer
|
23
|
+
* Int32Array - 32-bit 2's complement signed integer
|
24
|
+
* Uint32Array - 32-bit unsigned integer
|
25
|
+
* Float32Array - 32-bit IEEE floating point
|
26
|
+
* Float64Array - 64-bit IEEE floating point
|
27
|
+
|
28
|
+
== Type Conversion
|
29
|
+
|
30
|
+
Type Size Description Equivalent C Type Ruby Type
|
31
|
+
--------------------------------------------------------------------------------------------------
|
32
|
+
Int8Array 1 8-bit 2's complement signed integer signed char Fixnum
|
33
|
+
Uint8Array 1 8-bit unsigned integer unsigned char Fixnum
|
34
|
+
Int16Array 2 16-bit 2's complement signed integer short Fixnum
|
35
|
+
Uint16Array 2 16-bit unsigned integer unsigned short Fixnum
|
36
|
+
Int32Array 4 32-bit 2's complement signed integer int Fixnum / Bignum
|
37
|
+
Uint32Array 4 32-bit unsigned integer unsigned int Fixnum / Bignum
|
38
|
+
Float32Array 4 32-bit IEEE floating point float Float
|
39
|
+
Float64Array 8 64-bit IEEE floating point double Float
|
40
|
+
|
41
|
+
== Array Buffers
|
42
|
+
|
43
|
+
An opaque buffer with an explicit and fixed length. ArrayBuffer contents cannot be accessed directly - there's no Ruby API exposed to manipulate it.
|
44
|
+
|
45
|
+
buf = ArrayBuffer.new(8) => ArrayBuffer
|
46
|
+
buf.byte_length => 8
|
47
|
+
|
48
|
+
buf = ArrayBuffer.new("buffer") => ArrayBuffer
|
49
|
+
buf.byte_length => 6
|
50
|
+
buf.to_s => "buffer"
|
51
|
+
|
52
|
+
== Type Arrays
|
53
|
+
|
54
|
+
A group of types are used to create views of the ArrayBuffer. For example, to access the buffer as an array of 32-bit signed integers, an Int32Array would be created that refers to the ArrayBuffer.
|
55
|
+
|
56
|
+
A number of types are introduced that describe how to interpret the bytes in an ArrayBuffer. For example, an Int32Array views the bytes in an ArrayBuffer (or a subregion of an ArrayBuffer) as 32-bit signed integers. Elements of the array are accessible by getting or setting their index.
|
57
|
+
|
58
|
+
buf = ArrayBuffer.new("buffer") => ArrayBuffer
|
59
|
+
ary = Int32Array.new(buf) => Int32Array
|
60
|
+
ary.to_s => "buffer"
|
61
|
+
|
62
|
+
ary = Int32Array.new("01234567") => Int32Array
|
63
|
+
ary[1] = 23 => nil
|
64
|
+
ary[1] => 23
|
65
|
+
|
66
|
+
buf = ArrayBuffer.new(100) => ArrayBuffer
|
67
|
+
|
68
|
+
ary = Int32Array.new(buf, 20) => Int32Array
|
69
|
+
ary.length => 20
|
70
|
+
ary.byte_length => 80
|
71
|
+
ary.byte_offset => 20
|
72
|
+
|
73
|
+
ary = Int32Array.new(buf, 0, 20) => Int32Array
|
74
|
+
ary.length => 20
|
75
|
+
ary.byte_length => 80
|
76
|
+
ary.byte_offset => 0
|
77
|
+
|
78
|
+
ary = Int32Array.new(buf, 20, 20) => Int32Array
|
79
|
+
ary.length => 20
|
80
|
+
ary.byte_length => 80
|
81
|
+
ary.byte_offset => 20
|
82
|
+
|
83
|
+
ary = Int32Array.new("01234567") => Int32Array
|
84
|
+
ary.byte_length => 8
|
85
|
+
ary.to_s => "01234567"
|
86
|
+
|
87
|
+
ary = Int32Array.new(100) => Int32Array
|
88
|
+
ary.length => 100
|
89
|
+
ary.byte_length => 400
|
90
|
+
|
91
|
+
== Data Views
|
92
|
+
|
93
|
+
Multiple views can exist for the same ArrayBuffer, allowing for complex data structures to be built up, albeit with some difficulty. A DataView type is introduced which allows arbitrary indexed reads and writes of basic types from the bytes in the underlying ArrayBuffer. The goal is to allow as close to the native byte access as possible with very few performance penalties, while still retaining safety.
|
94
|
+
|
95
|
+
buf = ArrayBuffer.new(100) => ArrayBuffer
|
96
|
+
view = DataView.new(buf) => DataView
|
97
|
+
view.set_float64(2, 77.643) => nil
|
98
|
+
view.get_float64(2) => 758
|
99
|
+
|
100
|
+
buf = ArrayBuffer.new(100) => ArrayBuffer
|
101
|
+
view = DataView.new(buf) => DataView
|
102
|
+
view.set_uint32(2, 758) => nil
|
103
|
+
view.get_uint32(2) => 758
|
104
|
+
|
105
|
+
buf = ArrayBuffer.new(100) => ArrayBuffer
|
106
|
+
view = DataView.new(buf) => DataView
|
107
|
+
view.set_int16(2, 20) => nil
|
108
|
+
view.get_int16(2) => 20
|
109
|
+
|
110
|
+
== Requirements
|
111
|
+
|
112
|
+
* Known to work well on Linux, BSD variants and Mac OS X (not tested on Windows)
|
113
|
+
* A C compiler
|
114
|
+
* Ruby MRI 1.8 or 1.9, Rubinius or JRuby (versions 1.6 and up, C API is deprecated though)
|
115
|
+
|
116
|
+
== Installation
|
117
|
+
|
118
|
+
Rubygems installation
|
119
|
+
|
120
|
+
gem install type_array
|
121
|
+
|
122
|
+
Building from source
|
123
|
+
|
124
|
+
git clone git@github.com:methodmissing/type_array.git
|
125
|
+
rake
|
126
|
+
|
127
|
+
Running tests
|
128
|
+
|
129
|
+
rake test
|
130
|
+
|
131
|
+
== Todo
|
132
|
+
|
133
|
+
* Support Marshal dump / load
|
134
|
+
* Low level API for math operations on array elements without creating any Ruby objects
|
135
|
+
* Support structs / records
|
136
|
+
* Handle edges where coercion to and from Bignum is more appropriate
|
137
|
+
|
138
|
+
== Contact, feedback and bugs
|
139
|
+
|
140
|
+
This project is still work in progress. Please log bugs and suggestions at https://github.com/methodmissing/type_array/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'rake' unless defined?(Rake)
|
5
|
+
|
6
|
+
# Prefer compiled Rubinius bytecode in .rbx/
|
7
|
+
ENV["RBXOPT"] = "-Xrbc.db"
|
8
|
+
|
9
|
+
require 'rake/extensiontask'
|
10
|
+
require 'rake/testtask'
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'rdoc/task'
|
14
|
+
rescue LoadError # fallback to older 1.8.7 rubies
|
15
|
+
require 'rake/rdoctask'
|
16
|
+
end
|
17
|
+
|
18
|
+
gemspec = eval(IO.read('type_array.gemspec'))
|
19
|
+
|
20
|
+
Gem::PackageTask.new(gemspec) do |pkg|
|
21
|
+
end
|
22
|
+
|
23
|
+
Rake::ExtensionTask.new('type_array', gemspec) do |ext|
|
24
|
+
ext.name = 'type_array_ext'
|
25
|
+
ext.ext_dir = 'ext/type_array'
|
26
|
+
|
27
|
+
CLEAN.include 'lib/**/type_array_ext.*'
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Run TypeArray tests'
|
31
|
+
Rake::TestTask.new(:test) do |t|
|
32
|
+
t.test_files = Dir.glob("test/**/test_*.rb")
|
33
|
+
t.verbose = true
|
34
|
+
t.warning = true
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::RDocTask.new do |rd|
|
38
|
+
files = FileList["README.rdoc", "ext/type_array/*.c"]
|
39
|
+
rd.title = "TypeArray - Ruby implementation of the ECMAScript spec"
|
40
|
+
rd.main = "README.rdoc"
|
41
|
+
rd.rdoc_dir = "doc"
|
42
|
+
rd.options << "--promiscuous"
|
43
|
+
rd.rdoc_files.include(files)
|
44
|
+
end
|
45
|
+
|
46
|
+
task :test => :compile
|
47
|
+
task :default => :test
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#include "type_array_ext.h"
|
2
|
+
|
3
|
+
/*
|
4
|
+
* :nodoc:
|
5
|
+
* An Array Buffer implementation with a simple query API for it's chunk of managed memory. The buffer is fixed and
|
6
|
+
* cannot be resized. Data View and Type Array instances manipulate the buffer directly (this may change), but none
|
7
|
+
* of that's exposed through a Ruby API.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
|
11
|
+
VALUE rb_cArrayBuffer;
|
12
|
+
|
13
|
+
/*
|
14
|
+
* :nodoc:
|
15
|
+
* GC free callback
|
16
|
+
*
|
17
|
+
*/
|
18
|
+
static void rb_free_array_buffer(void *ptr)
|
19
|
+
{
|
20
|
+
rb_array_buffer_t *buf = (rb_array_buffer_t *)ptr;
|
21
|
+
#ifdef TYPE_ARRAY_DEBUG
|
22
|
+
printf(">> rb_free_array_buffer %p\n", ptr);
|
23
|
+
#endif
|
24
|
+
if (buf) {
|
25
|
+
if (buf->buf) xfree(buf->buf);
|
26
|
+
xfree(buf);
|
27
|
+
buf = NULL;
|
28
|
+
}
|
29
|
+
#ifdef TYPE_ARRAY_DEBUG
|
30
|
+
printf("<< rb_free_array_buffer %p\n", ptr);
|
31
|
+
#endif
|
32
|
+
}
|
33
|
+
|
34
|
+
/*
|
35
|
+
* :nodoc:
|
36
|
+
* Utility function to allocate a new ArrayBuffer instance of given length. An optional pointer to an existing chunk
|
37
|
+
* of memory may be passed, which becomes the buffer contents. This is useful for reading raw binary data from
|
38
|
+
* standard I/O or sockets as binary strings.
|
39
|
+
*
|
40
|
+
*/
|
41
|
+
VALUE rb_alloc_array_buffer(unsigned long length, void *data)
|
42
|
+
{
|
43
|
+
VALUE buffer;
|
44
|
+
rb_array_buffer_t *buf = NULL;
|
45
|
+
if (length == 0) rb_raise(rb_eRangeError, "ArrayBuffer is not a small enough positive integer");
|
46
|
+
buffer = Data_Make_Struct(rb_cArrayBuffer, rb_array_buffer_t, 0, rb_free_array_buffer, buf);
|
47
|
+
buf->length = length;
|
48
|
+
buf->buf = xcalloc(buf->length, 1);
|
49
|
+
if (!buf->buf) rb_raise(rb_eRangeError, "Unable to allocate ArrayBuffer");
|
50
|
+
if (data) memmove(buf->buf, data, length);
|
51
|
+
rb_obj_call_init(buffer, 0, NULL);
|
52
|
+
rb_obj_freeze(buffer);
|
53
|
+
return buffer;
|
54
|
+
}
|
55
|
+
|
56
|
+
/*
|
57
|
+
* :nodoc:
|
58
|
+
* Utility function to create a new ArrayBuffer instance from a begin..end byte range
|
59
|
+
*
|
60
|
+
*/
|
61
|
+
static VALUE rb_copy_array_buffer(rb_array_buffer_t *source, long begin, long end)
|
62
|
+
{
|
63
|
+
long length;
|
64
|
+
void *data;
|
65
|
+
length = (end - begin);
|
66
|
+
if (length < 0) length = 0;
|
67
|
+
data = source + begin;
|
68
|
+
return rb_alloc_array_buffer(length, data);
|
69
|
+
}
|
70
|
+
|
71
|
+
/*
|
72
|
+
* call-seq:
|
73
|
+
* ArrayBuffer.new(8) => ArrayBuffer
|
74
|
+
* ArrayBuffer.new("buffer") => ArrayBuffer
|
75
|
+
*
|
76
|
+
* Creates a new ArrayBuffer instance. Both length and data (String) constructors are supported.
|
77
|
+
*
|
78
|
+
* === Examples
|
79
|
+
* buf = ArrayBuffer.new(8) => ArrayBuffer
|
80
|
+
* buf.byte_length => 8
|
81
|
+
*
|
82
|
+
* buf = ArrayBuffer.new("buffer") => ArrayBuffer
|
83
|
+
* buf.byte_length => 6
|
84
|
+
* buf.to_s => "buffer"
|
85
|
+
*
|
86
|
+
*/
|
87
|
+
static VALUE rb_array_buffer_s_new(VALUE klass, VALUE obj)
|
88
|
+
{
|
89
|
+
VALUE buffer;
|
90
|
+
if (FIXNUM_P(obj)) {
|
91
|
+
buffer = rb_alloc_array_buffer(FIX2ULONG(obj), NULL);
|
92
|
+
} else if (rb_type(obj) == T_STRING) {
|
93
|
+
ArrayBufferEncode(obj);
|
94
|
+
buffer = rb_alloc_array_buffer((unsigned long)RSTRING_LEN(obj), (void *)RSTRING_PTR(obj));
|
95
|
+
} else {
|
96
|
+
rb_raise(rb_eTypeError, "ArrayBuffer constructor %s not supported.", RSTRING_PTR(rb_obj_as_string(obj)));
|
97
|
+
}
|
98
|
+
return buffer;
|
99
|
+
}
|
100
|
+
|
101
|
+
/*
|
102
|
+
* call-seq:
|
103
|
+
* buf.byte_length => Fixnum
|
104
|
+
*
|
105
|
+
* Returns the size of the buffer.
|
106
|
+
*
|
107
|
+
* === Examples
|
108
|
+
* buf = ArrayBuffer.new(8) => ArrayBuffer
|
109
|
+
* buf.byte_length => 8
|
110
|
+
*
|
111
|
+
*/
|
112
|
+
static VALUE rb_array_buffer_byte_length(VALUE obj)
|
113
|
+
{
|
114
|
+
GetArrayBuffer(obj);
|
115
|
+
return ULONG2NUM(buf->length);
|
116
|
+
}
|
117
|
+
|
118
|
+
/*
|
119
|
+
* call-seq:
|
120
|
+
* buf.slice(1) => ArrayBuffer
|
121
|
+
*
|
122
|
+
* Returns a new ArrayBuffer instance with a slice (copy) of a range of memory managed by the source buffer.
|
123
|
+
*
|
124
|
+
* === Examples
|
125
|
+
* buf = ArrayBuffer.new(8) => ArrayBuffer
|
126
|
+
* buf.slice(2) => ArrayBuffer
|
127
|
+
* buf.slice(4,6) => ArrayBuffer
|
128
|
+
*
|
129
|
+
*/
|
130
|
+
static VALUE rb_array_buffer_slice(int argc, VALUE *argv, VALUE obj)
|
131
|
+
{
|
132
|
+
VALUE buffer, begin, end;
|
133
|
+
long b = 0;
|
134
|
+
long e = 0;
|
135
|
+
GetArrayBuffer(obj);
|
136
|
+
rb_scan_args(argc, argv, "11", &begin, &end);
|
137
|
+
Check_Type(begin, T_FIXNUM);
|
138
|
+
b = FIX2LONG(begin);
|
139
|
+
if (NIL_P(end)) {
|
140
|
+
e = buf->length;
|
141
|
+
} else {
|
142
|
+
Check_Type(end, T_FIXNUM);
|
143
|
+
e = FIX2LONG(end);
|
144
|
+
}
|
145
|
+
if (b < 0) b = buf->length - abs(b);
|
146
|
+
if (e < 0) e = buf->length - abs(e);
|
147
|
+
|
148
|
+
if (e > buf->length) rb_raise(rb_eRangeError, "Offset out of range.");
|
149
|
+
buffer = rb_copy_array_buffer(buf, b, e);
|
150
|
+
return buffer;
|
151
|
+
}
|
152
|
+
|
153
|
+
/*
|
154
|
+
* call-seq:
|
155
|
+
* buf.to_s => String
|
156
|
+
*
|
157
|
+
* Returns a String (binary) representation of the buffer.
|
158
|
+
*
|
159
|
+
* === Examples
|
160
|
+
* buf = ArrayBuffer.new("buffer") => ArrayBuffer
|
161
|
+
* buf.byte_length => 6
|
162
|
+
* buf.to_s => "buffer"
|
163
|
+
*
|
164
|
+
*/
|
165
|
+
VALUE rb_array_buffer_to_s(VALUE obj)
|
166
|
+
{
|
167
|
+
VALUE str;
|
168
|
+
GetArrayBuffer(obj);
|
169
|
+
str = rb_str_new((const char*)buf->buf, buf->length);
|
170
|
+
ArrayBufferEncode(str);
|
171
|
+
return rb_obj_freeze(str);
|
172
|
+
}
|
173
|
+
|
174
|
+
void _init_array_buffer()
|
175
|
+
{
|
176
|
+
rb_cArrayBuffer = rb_define_class("ArrayBuffer", rb_cObject);
|
177
|
+
|
178
|
+
rb_define_singleton_method(rb_cArrayBuffer, "new", rb_array_buffer_s_new, 1);
|
179
|
+
|
180
|
+
rb_define_method(rb_cArrayBuffer, "byte_length", rb_array_buffer_byte_length, 0);
|
181
|
+
rb_define_method(rb_cArrayBuffer, "slice", rb_array_buffer_slice, -1);
|
182
|
+
rb_define_method(rb_cArrayBuffer, "to_s", rb_array_buffer_to_s, 0);
|
183
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#ifndef ARRAY_BUFFER_H
|
2
|
+
#define ARRAY_BUFFER_H
|
3
|
+
|
4
|
+
typedef struct {
|
5
|
+
unsigned long length;
|
6
|
+
void *buf;
|
7
|
+
} rb_array_buffer_t;
|
8
|
+
|
9
|
+
#define GetArrayBuffer(obj) \
|
10
|
+
rb_array_buffer_t *buf = NULL; \
|
11
|
+
Data_Get_Struct(obj, rb_array_buffer_t, buf); \
|
12
|
+
if (!buf) rb_raise(rb_eTypeError, "uninitialized ArrayBuffer!");
|
13
|
+
|
14
|
+
VALUE rb_alloc_array_buffer(unsigned long length, void *data);
|
15
|
+
VALUE rb_array_buffer_to_s(VALUE obj);
|
16
|
+
|
17
|
+
void _init_array_buffer();
|
18
|
+
|
19
|
+
#endif
|