c_buffer 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +22 -0
- data/ext/c_buffer/c_buffer.c +166 -0
- data/ext/c_buffer/c_buffer.h +42 -0
- data/lib/c_buffer.rb +79 -0
- data/lib/c_buffer/c_buffer.dll +0 -0
- data/lib/c_buffer/c_buffer.so +0 -0
- data/rakefile.rb +32 -0
- data/test/test_c_buffer.rb +17 -0
- metadata +64 -0
data/README
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# CBuffer
|
4
|
+
#
|
5
|
+
# Simple circular buffer for staging data before writing to disk
|
6
|
+
#
|
7
|
+
# author hugo benichi
|
8
|
+
# email hugo.benichi@m4x.org
|
9
|
+
# copyright 2012 hugo benichi
|
10
|
+
# version 0.1.0
|
11
|
+
#
|
12
|
+
##
|
13
|
+
|
14
|
+
installation:
|
15
|
+
|
16
|
+
run "rake install" in the root directory
|
17
|
+
it will compile the gem library and produce a .gem package for ruby
|
18
|
+
it will then install the .gem automatically
|
19
|
+
|
20
|
+
usage, test:
|
21
|
+
|
22
|
+
cf test/test_cbuffer.rb
|
@@ -0,0 +1,166 @@
|
|
1
|
+
/*
|
2
|
+
*
|
3
|
+
* CBuffer
|
4
|
+
*
|
5
|
+
* Simple circular buffer for staging data before writing to disk
|
6
|
+
*
|
7
|
+
* author hugo benichi
|
8
|
+
* email hugo.benichi@m4x.org
|
9
|
+
* copyright 2012 hugo benichi
|
10
|
+
* version 0.1.1
|
11
|
+
*
|
12
|
+
*/
|
13
|
+
|
14
|
+
|
15
|
+
#include <stdlib.h>
|
16
|
+
#include <stdio.h>
|
17
|
+
#include <c_buffer.h>
|
18
|
+
#include <assert.h>
|
19
|
+
|
20
|
+
|
21
|
+
DLL cbuffer*
|
22
|
+
cbuffer_new(
|
23
|
+
int chunk, // the size in byte of one chunk of buffer memory
|
24
|
+
int size // the total size in byte of the buffer, at creation
|
25
|
+
)
|
26
|
+
{
|
27
|
+
assert(chunk > 0);
|
28
|
+
assert(size > 0);
|
29
|
+
|
30
|
+
int n = size / chunk;
|
31
|
+
if (!n) n = 2;
|
32
|
+
|
33
|
+
link* lnk_ary = malloc( n * sizeof(link) );
|
34
|
+
|
35
|
+
if ( !lnk_ary )
|
36
|
+
fprintf(stderr,"cbuffer.c: error while allocating memory for new cbuffer\n");
|
37
|
+
|
38
|
+
int i = 0;
|
39
|
+
while( n - i)
|
40
|
+
{
|
41
|
+
lnk_ary[i].mem = malloc(chunk);
|
42
|
+
if( !( lnk_ary[i].mem ) )
|
43
|
+
fprintf(stderr,"cbuffer.c: error while allocating memory for new cbuffer\n");
|
44
|
+
if(i+1 < n)
|
45
|
+
lnk_ary[i].next = &lnk_ary[i+1];
|
46
|
+
i++;
|
47
|
+
}
|
48
|
+
lnk_ary[n-1].next = lnk_ary;
|
49
|
+
|
50
|
+
|
51
|
+
cbuffer* cbuf = malloc(sizeof(cbuffer));
|
52
|
+
if ( !cbuf )
|
53
|
+
fprintf(stderr,"cbuffer.c: error while allocating memory for new cbuffer\n");
|
54
|
+
|
55
|
+
cbuf->inbound = lnk_ary; // start feeding at mem[0]
|
56
|
+
//cbuf->start = lnk_ary; // remember mem[0]
|
57
|
+
cbuf->start = NULL; // remember mem[0]
|
58
|
+
cbuf->outbound = NULL; // null until inbound != start
|
59
|
+
cbuf->stop = NULL; // when not null inbound as stopped at stop
|
60
|
+
cbuf->output = NULL;
|
61
|
+
cbuf->chunk = chunk;
|
62
|
+
|
63
|
+
return cbuf;
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
DLL void
|
68
|
+
cbuffer_free(cbuffer* buf)
|
69
|
+
{
|
70
|
+
link* l = buf->start;
|
71
|
+
link* s = buf->start;
|
72
|
+
while( l != s)
|
73
|
+
{
|
74
|
+
free(l->mem);
|
75
|
+
l = l->next;
|
76
|
+
}
|
77
|
+
free( buf->start );
|
78
|
+
free( buf );
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
DLL void
|
83
|
+
cbuffer_open(
|
84
|
+
cbuffer* buf,
|
85
|
+
const char* address
|
86
|
+
)
|
87
|
+
{
|
88
|
+
buf-> output = fopen(address, "w" );
|
89
|
+
|
90
|
+
if( !(buf->output) )
|
91
|
+
fprintf(stderr, "cbuffer.c: error while opening file %s\n", address);
|
92
|
+
|
93
|
+
}
|
94
|
+
|
95
|
+
|
96
|
+
DLL char*
|
97
|
+
cbuffer_next(cbuffer* buf)
|
98
|
+
{
|
99
|
+
if ( buf->start ) // wait until the next buffer is free
|
100
|
+
{
|
101
|
+
while( buf->inbound->next == buf->outbound ); //busy wait until the next buffer is free
|
102
|
+
buf->inbound = buf->inbound->next;
|
103
|
+
} else {
|
104
|
+
buf->start = buf->inbound;
|
105
|
+
}
|
106
|
+
return buf->inbound->mem;
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
DLL char*
|
111
|
+
cbuffer_has_next(cbuffer* buf)
|
112
|
+
{
|
113
|
+
char* out = NULL;
|
114
|
+
if ( buf->start )
|
115
|
+
{
|
116
|
+
if( buf->inbound != buf->outbound )
|
117
|
+
{
|
118
|
+
buf->inbound = buf->inbound->next;
|
119
|
+
out = buf->inbound->mem;
|
120
|
+
}
|
121
|
+
} else {
|
122
|
+
buf->start = buf->inbound;
|
123
|
+
out = buf->inbound->mem;
|
124
|
+
}
|
125
|
+
return out;
|
126
|
+
}
|
127
|
+
|
128
|
+
|
129
|
+
DLL void
|
130
|
+
cbuffer_stop(cbuffer* buf)
|
131
|
+
{
|
132
|
+
buf->stop = buf->inbound;
|
133
|
+
buf->inbound = buf->inbound->next;
|
134
|
+
}
|
135
|
+
|
136
|
+
|
137
|
+
DLL int
|
138
|
+
cbuffer_write(cbuffer* buf)
|
139
|
+
{
|
140
|
+
while( !( buf->start ) || buf->inbound == buf->start);
|
141
|
+
// busy wait while the first buffer is written
|
142
|
+
|
143
|
+
buf->outbound = buf->start;
|
144
|
+
|
145
|
+
int count = 0;
|
146
|
+
while( !( buf->stop ) )
|
147
|
+
{
|
148
|
+
fwrite( buf->outbound->mem, 1, buf-> chunk, buf-> output);
|
149
|
+
count += buf->chunk;
|
150
|
+
|
151
|
+
buf->outbound = buf->outbound->next;
|
152
|
+
while(buf->outbound == buf->inbound); //busy wait
|
153
|
+
}
|
154
|
+
|
155
|
+
while(1)
|
156
|
+
{
|
157
|
+
fwrite( buf->outbound->mem, 1, buf-> chunk, buf-> output);
|
158
|
+
count += 1;
|
159
|
+
if( buf->stop == buf->outbound )
|
160
|
+
break;
|
161
|
+
buf->outbound = buf->outbound->next;
|
162
|
+
}
|
163
|
+
|
164
|
+
fclose(buf->output);
|
165
|
+
return count;
|
166
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
|
3
|
+
|
4
|
+
#if defined(_WIN32)
|
5
|
+
#define DLL extern __declspec(dllexport)
|
6
|
+
#else
|
7
|
+
#define DLL
|
8
|
+
#endif
|
9
|
+
|
10
|
+
|
11
|
+
typedef struct cbuffer cbuffer;
|
12
|
+
typedef struct link link;
|
13
|
+
|
14
|
+
|
15
|
+
struct
|
16
|
+
cbuffer // 192 bytes on 32bits machines and 352 bytes on 64bits machines
|
17
|
+
{
|
18
|
+
link* inbound; // next buffer memory for incoming data
|
19
|
+
link* outbound; // next buffer memory for writing
|
20
|
+
link* start; // start of the memory (used for cleaning and init)
|
21
|
+
link* stop; // where to write to
|
22
|
+
FILE* output; // file
|
23
|
+
int chunk; // size of one chunk
|
24
|
+
};
|
25
|
+
|
26
|
+
|
27
|
+
struct
|
28
|
+
link
|
29
|
+
{
|
30
|
+
char* mem;
|
31
|
+
link* next;
|
32
|
+
};
|
33
|
+
|
34
|
+
|
35
|
+
DLL cbuffer* cbuffer_new (int chunk, int size); // size is n_chunk * chunk
|
36
|
+
DLL void cbuffer_free (cbuffer*); // don't forget to close output if open
|
37
|
+
DLL void cbuffer_open (cbuffer*, const char* ); // open a new file at the given path
|
38
|
+
|
39
|
+
DLL char* cbuffer_next (cbuffer*); // blocking, return the next buffer memory inline
|
40
|
+
DLL char* cbuffer_has_next (cbuffer*); // non-blocking, return null if not ready
|
41
|
+
DLL void cbuffer_stop (cbuffer*); // flag for stopping (cut the circular list)
|
42
|
+
DLL int cbuffer_write (cbuffer*); // blocking, write everything untill stop
|
data/lib/c_buffer.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# CBuffer
|
4
|
+
#
|
5
|
+
# Simple circular buffer for staging data before writing to disk
|
6
|
+
#
|
7
|
+
# author hugo benichi
|
8
|
+
# email hugo.benichi@m4x.org
|
9
|
+
# copyright 2012 hugo benichi
|
10
|
+
# version 0.1.1
|
11
|
+
#
|
12
|
+
##
|
13
|
+
|
14
|
+
# Main class and namespace of the program
|
15
|
+
# This class should be instancied to use a buffer
|
16
|
+
class CBuffer
|
17
|
+
|
18
|
+
require 'ffi'
|
19
|
+
|
20
|
+
# the API module wraps the native functions of cbuffer.c
|
21
|
+
module API
|
22
|
+
extend FFI::Library
|
23
|
+
lib_type = ENV["OS"] ? "dll" : "so"
|
24
|
+
#ffi_lib $LOAD_PATH.map{ |p| p + "/cbuffer/cbuffer.#{lib_type}" }
|
25
|
+
ffi_lib File.dirname(__FILE__) + "/c_buffer/c_buffer.#{lib_type}"
|
26
|
+
[
|
27
|
+
[ :cbuffer_new, [:int32, :int32], :pointer ],
|
28
|
+
[ :cbuffer_free, [:pointer], :void ],
|
29
|
+
[ :cbuffer_open, [:pointer, :pointer], :void ],
|
30
|
+
[ :cbuffer_next, [:pointer], :pointer, :blocking => true ],
|
31
|
+
[ :cbuffer_has_next, [:pointer], :pointer ],
|
32
|
+
[ :cbuffer_stop, [:pointer], :void ],
|
33
|
+
[ :cbuffer_write, [:pointer], :int32, :blocking => true ],
|
34
|
+
].each{ |sig| attach_function *sig }
|
35
|
+
end
|
36
|
+
|
37
|
+
# the CBufferRaw class wraps the native struct memory used to store the buffer memory
|
38
|
+
# not used at the momentm here for reference only
|
39
|
+
class CBufferRaw < FFI::Struct
|
40
|
+
layout :inbound, :pointer,
|
41
|
+
:outbound, :pointer,
|
42
|
+
:start, :pointer,
|
43
|
+
:stop, :pointer,
|
44
|
+
:output, :pointer,
|
45
|
+
:chunk, :int
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize args
|
49
|
+
chunk = args[:chunk] || 1
|
50
|
+
length = args[:length] || 2
|
51
|
+
path = args[:path] || 1
|
52
|
+
@struct = API::cbuffer_new chunk, length
|
53
|
+
API::cbuffer_open @struct, path
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def write &block
|
58
|
+
running = true
|
59
|
+
self.define_singleton_method :stop do
|
60
|
+
API.cbuffer_stop @struct
|
61
|
+
sleep 1 while running
|
62
|
+
API.cbuffer_free @struct
|
63
|
+
end
|
64
|
+
Thread.new do
|
65
|
+
begin
|
66
|
+
count = API.cbuffer_write @struct
|
67
|
+
running = false
|
68
|
+
block.call(count) if block
|
69
|
+
catch
|
70
|
+
puts "error in writing thread"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def next
|
76
|
+
API.cbuffer_next @struct
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
Binary file
|
Binary file
|
data/rakefile.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
name = "c_buffer"
|
3
|
+
|
4
|
+
lib_type = ENV["OS"] ? "dll" : "so"
|
5
|
+
lib_file = "./lib/%s/%s.%s" % [name,name,lib_type]
|
6
|
+
src_file = "./ext/%s/%s.c" % [name,name]
|
7
|
+
hdr_path = "./ext/%s" % name
|
8
|
+
|
9
|
+
flags = ENV["OS"] ? "-D _WIN32" : "-fPIC"
|
10
|
+
|
11
|
+
task :compile => lib_file
|
12
|
+
|
13
|
+
task lib_file => src_file do
|
14
|
+
sh "gcc -shared -o #{lib_file} #{src_file} -I#{hdr_path} #{flags}"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :test_global do
|
18
|
+
ruby "test/test_%s.rb" % name
|
19
|
+
end
|
20
|
+
|
21
|
+
task :test_local do
|
22
|
+
ruby "-I./lib test/test_%s.rb" % name
|
23
|
+
end
|
24
|
+
|
25
|
+
task :gem_install => :gem_build do
|
26
|
+
gemfile = Dir.new("./").entries.select{ |f| f =~ /cbuffer-[\d]+\.[\d]+\.[\d]+.gem/ }[0]
|
27
|
+
sh "gem install %s" % gemfile
|
28
|
+
end
|
29
|
+
|
30
|
+
task :gem_build => :compile do
|
31
|
+
sh "gem build %s.gemspec" % name
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'c_buffer'
|
2
|
+
|
3
|
+
repet = 126
|
4
|
+
chunk = 1024*126
|
5
|
+
length = chunk * 64
|
6
|
+
|
7
|
+
|
8
|
+
buf = CBuffer.new( path: "./data", chunk: chunk, length: length )
|
9
|
+
buf.write{ |count| puts "wrote #{count} bytes of data" }
|
10
|
+
|
11
|
+
data = Array.new(chunk){rand 255}
|
12
|
+
|
13
|
+
repet.times do |i|
|
14
|
+
buf.next.put_array_of_uchar 0, data
|
15
|
+
end
|
16
|
+
|
17
|
+
buf.stop
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: c_buffer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hugo Benichi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi
|
16
|
+
requirement: &19284820 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *19284820
|
25
|
+
description: A simple circular buffer and its Ruby interface to go along RbScope and
|
26
|
+
RbVisa. Perfect for staging high throughput input data to low throughput I/O.
|
27
|
+
email: hugo.benichi@m4x.org
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- lib/c_buffer.rb
|
33
|
+
- lib/c_buffer/c_buffer.so
|
34
|
+
- lib/c_buffer/c_buffer.dll
|
35
|
+
- ext/c_buffer/c_buffer.c
|
36
|
+
- ext/c_buffer/c_buffer.h
|
37
|
+
- rakefile.rb
|
38
|
+
- README
|
39
|
+
- test/test_c_buffer.rb
|
40
|
+
homepage: http://github.com/hugobenichi/cbuffer
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.11
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: A simple circular buffer and its Ruby interface
|
64
|
+
test_files: []
|