async-ruby-zip 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ git (1.2.5)
6
+ jeweler (1.8.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rdoc
11
+ json (1.7.3)
12
+ rake (0.9.2.2)
13
+ rdoc (3.12)
14
+ json (~> 1.4)
15
+ rspec (2.10.0)
16
+ rspec-core (~> 2.10.0)
17
+ rspec-expectations (~> 2.10.0)
18
+ rspec-mocks (~> 2.10.0)
19
+ rspec-core (2.10.1)
20
+ rspec-expectations (2.10.0)
21
+ diff-lcs (~> 1.1.3)
22
+ rspec-mocks (2.10.1)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ bundler
29
+ jeweler (~> 1.8.3)
30
+ rdoc (~> 3.12)
31
+ rspec
@@ -0,0 +1,30 @@
1
+ async-ruby-zip
2
+ ==============
3
+
4
+ Non-blocking zip reading and writing for Ruby
5
+
6
+
7
+ ## Requirements.
8
+
9
+ * OS X or Linux
10
+ * MRI 1.9.2
11
+ * libzip >=0.10.1
12
+
13
+ ## Example.
14
+
15
+ ```ruby
16
+ require 'rubygems'
17
+ require 'async_zip'
18
+ include AsyncZip
19
+
20
+
21
+ # Non-blocking zip-file creation:
22
+ AsyncZip.create(files, './output.zip') do |task|
23
+ puts task.inspect
24
+ end
25
+
26
+ # Non-blocking zip-file extraction:
27
+ AsyncZip.extract('./output.zip', './extracted') do |task|
28
+ puts task.inspect
29
+ end
30
+
@@ -0,0 +1,77 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "async-ruby-zip"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Grigoriy Chudnov"]
12
+ s.date = "2012-08-04"
13
+ s.description = "Non-blocking zip reading and writing for Ruby."
14
+ s.email = "g.chudnov@gmail.com"
15
+ s.extensions = ["ext/extconf.rb"]
16
+ s.extra_rdoc_files = [
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile.lock",
21
+ "README.md",
22
+ "async-ruby-zip-1.0.0.gem",
23
+ "async-ruby-zip.gemspec",
24
+ "ext/LICENSE",
25
+ "ext/archive.c",
26
+ "ext/archive.h",
27
+ "ext/archive_data.c",
28
+ "ext/archive_data.h",
29
+ "ext/archive_data_fwd.h",
30
+ "ext/async_zip.c",
31
+ "ext/async_zip.h",
32
+ "ext/callback.c",
33
+ "ext/callback.h",
34
+ "ext/carchive.c",
35
+ "ext/carchive.h",
36
+ "ext/carray.c",
37
+ "ext/carray.h",
38
+ "ext/cerror.c",
39
+ "ext/cerror.h",
40
+ "ext/cfilesystem.c",
41
+ "ext/cfilesystem.h",
42
+ "ext/extconf.rb",
43
+ "ext/task.c",
44
+ "ext/task.h",
45
+ "ext/writer.c",
46
+ "ext/writer.h",
47
+ "lib/async_zip.rb",
48
+ "lib/async_zip/version.rb"
49
+ ]
50
+ s.homepage = "https://github.com/gchudnov/async-ruby-zip"
51
+ s.licenses = ["MIT"]
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = "1.8.24"
54
+ s.summary = "async-ruby-zip is a ruby extension that zip files asynchronously."
55
+
56
+ if s.respond_to? :specification_version then
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
60
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
61
+ s.add_development_dependency(%q<bundler>, [">= 0"])
62
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
63
+ s.add_development_dependency(%q<rspec>, [">= 0"])
64
+ else
65
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
66
+ s.add_dependency(%q<bundler>, [">= 0"])
67
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
68
+ s.add_dependency(%q<rspec>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
72
+ s.add_dependency(%q<bundler>, [">= 0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
74
+ s.add_dependency(%q<rspec>, [">= 0"])
75
+ end
76
+ end
77
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Grigoriy Chudnov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,106 @@
1
+ #include "archive.h"
2
+ #include "carchive.h"
3
+ #include "cerror.h"
4
+ #include "carray.h"
5
+ #include "archive_data.h"
6
+ #include "callback.h"
7
+ #include "writer.h"
8
+
9
+
10
+ static VALUE az_create(VALUE self, VALUE zip_path, VALUE files);
11
+ static VALUE az_extract(VALUE self, VALUE zip_path, VALUE dest_path);
12
+
13
+
14
+ static void* az_archive_thread_func(void* data)
15
+ {
16
+ archive_data_t* adata = (archive_data_t*)data;
17
+ if(!adata)
18
+ return NULL;
19
+
20
+ cerror_t err = { 0 };
21
+
22
+ if(adata->zip_path && !adata->dst_path) // create
23
+ {
24
+ err = carchive_create(adata->zip_path, adata->files_arr);
25
+ }
26
+ else if(adata->zip_path && adata->dst_path) // extract
27
+ {
28
+ err = carchive_extract(adata->zip_path, adata->dst_path, &adata->files_arr);
29
+ }
30
+
31
+ // error
32
+ if(cerror_is_error(&err))
33
+ {
34
+ adata->err_str = strdup(err.message);
35
+ }
36
+ cerror_free_message(&err);
37
+
38
+ az_add_to_event_qeueue(adata);
39
+
40
+ return NULL;
41
+ }
42
+
43
+
44
+ /* Add files to archive */
45
+ static VALUE az_create(VALUE self, VALUE files, VALUE zip_path)
46
+ {
47
+ rb_need_block();
48
+ VALUE proc = rb_block_proc();
49
+
50
+ int len = RARRAY_LEN(files);
51
+ if(len > 0)
52
+ {
53
+ carray_str_t* parr = carray_str_create(len);
54
+
55
+ int i;
56
+ for(i = 0; i != len; ++i)
57
+ {
58
+ VALUE current = rb_ary_entry(files, i);
59
+ if(rb_respond_to(current, rb_intern("to_s")))
60
+ {
61
+ VALUE name = rb_funcall(current, rb_intern("to_s"), 0);
62
+
63
+ carray_str_set(parr, i, StringValuePtr(name));
64
+ }
65
+ else
66
+ {
67
+ carray_str_set(parr, i, NULL);
68
+ }
69
+ }
70
+
71
+ archive_data_t* adata = az_make_archive_data(StringValuePtr(zip_path), NULL, parr);
72
+ adata->proc = proc;
73
+
74
+ rb_gc_register_address(&adata->proc);
75
+
76
+ //
77
+ az_enqueue_task(az_archive_thread_func, adata);
78
+ }
79
+
80
+ return self;
81
+ }
82
+
83
+ /* Extract files from archive */
84
+ static VALUE az_extract(VALUE self, VALUE zip_path, VALUE dest_path)
85
+ {
86
+ rb_need_block();
87
+ VALUE proc = rb_block_proc();
88
+
89
+ archive_data_t* adata = az_make_archive_data(StringValuePtr(zip_path), StringValuePtr(dest_path), NULL);
90
+ adata->proc = proc;
91
+
92
+ rb_gc_register_address(&adata->proc);
93
+
94
+ //
95
+ az_enqueue_task(az_archive_thread_func, adata);
96
+
97
+ return self;
98
+ }
99
+
100
+
101
+ /* Initialize zip pipeline */
102
+ void init_async_zip_archive(void)
103
+ {
104
+ rb_define_singleton_method(mAsyncZip, "create", az_create, 2);
105
+ rb_define_singleton_method(mAsyncZip, "extract", az_extract, 2);
106
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef ASYNC_ZIP_ARCHIVE_H
2
+ #define ASYNC_ZIP_ARCHIVE_H
3
+
4
+ #include "async_zip.h"
5
+
6
+ void init_async_zip_archive();
7
+
8
+ #endif
@@ -0,0 +1,61 @@
1
+ #include "archive_data.h"
2
+
3
+ // Create a new archive-data structure
4
+ archive_data_t* az_make_archive_data(const char* zip_path, const char* dst_path, carray_str_t* files_arr)
5
+ {
6
+ archive_data_t* adata = (archive_data_t*)malloc(sizeof(archive_data_t));
7
+ if(adata)
8
+ {
9
+ memset(adata, 0, sizeof(archive_data_t));
10
+
11
+ if(zip_path)
12
+ {
13
+ adata->zip_path = strdup(zip_path);
14
+ }
15
+
16
+ if(dst_path)
17
+ {
18
+ adata->dst_path = strdup(dst_path);
19
+ }
20
+
21
+ if(files_arr)
22
+ {
23
+ adata->files_arr = files_arr;
24
+ }
25
+ }
26
+
27
+ return adata;
28
+ }
29
+
30
+ // Free archive-data structure
31
+ void az_free_archive_data(archive_data_t* adata)
32
+ {
33
+ if(!adata)
34
+ return;
35
+
36
+ if(adata->zip_path)
37
+ {
38
+ free(adata->zip_path);
39
+ adata->zip_path = NULL;
40
+ }
41
+
42
+ if(adata->dst_path)
43
+ {
44
+ free(adata->dst_path);
45
+ adata->dst_path = NULL;
46
+ }
47
+
48
+ if(adata->err_str)
49
+ {
50
+ free(adata->err_str);
51
+ adata->err_str = NULL;
52
+ }
53
+
54
+ if(adata->files_arr)
55
+ {
56
+ carray_str_destroy(adata->files_arr);
57
+ adata->files_arr = NULL;
58
+ }
59
+
60
+ free(adata);
61
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef ASYNC_ZIP_ARCHIVE_DATA_H
2
+ #define ASYNC_ZIP_ARCHIVE_DATA_H
3
+
4
+ #include "ruby.h"
5
+ #include "carray.h"
6
+
7
+ typedef struct _archive_data_t
8
+ {
9
+ char* zip_path; // source zip-file
10
+ char* dst_path; // destination folder to extract zip-file { used only for extraction }
11
+
12
+ carray_str_t* files_arr; // array of files to archive or files that were extracted
13
+
14
+ VALUE proc;
15
+ char* err_str;
16
+
17
+ struct _archive_data_t* next;
18
+ } archive_data_t;
19
+
20
+
21
+ archive_data_t* az_make_archive_data(const char* zip_path, const char* dst_path, carray_str_t* files_arr);
22
+ void az_free_archive_data(archive_data_t* adata);
23
+
24
+ #endif
25
+
@@ -0,0 +1,6 @@
1
+ #ifndef ASYNC_ZIP_ARCHIVE_DATA_FWD_H
2
+ #define ASYNC_ZIP_ARCHIVE_DATA_FWD_H
3
+
4
+ struct archive_data_t;
5
+
6
+ #endif
@@ -0,0 +1,18 @@
1
+ #include "async_zip.h"
2
+ #include "archive.h"
3
+ #include "task.h"
4
+ #include "callback.h"
5
+
6
+ VALUE mAsyncZip, eZipError;
7
+
8
+ /* Initialize extension */
9
+ void Init_async_zip_ext()
10
+ {
11
+ mAsyncZip = rb_define_module("AsyncZip");
12
+ eZipError = rb_define_class_under(mAsyncZip, "Error", rb_eStandardError);
13
+
14
+ //
15
+ init_async_zip_archive();
16
+ init_async_zip_task();
17
+ init_async_zip_event_thread();
18
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef ASYNC_ZIP_H
2
+ #define ASYNC_ZIP_H
3
+
4
+ #include "ruby.h"
5
+
6
+ extern VALUE mAsyncZip, eZipError;
7
+
8
+ #endif
@@ -0,0 +1,121 @@
1
+ #include "callback.h"
2
+ #include "async_zip.h"
3
+ #include "task.h"
4
+ #include "archive_data.h"
5
+ #include <pthread.h>
6
+
7
+
8
+ typedef struct _adata_wait_t
9
+ {
10
+ archive_data_t* adata;
11
+ int abort;
12
+ } adata_wait_t;
13
+
14
+
15
+ /* Queue of callbacks; each one invoked on a ruby thread */
16
+ pthread_mutex_t az_proc_mutex = PTHREAD_MUTEX_INITIALIZER;
17
+ pthread_cond_t az_proc_cond = PTHREAD_COND_INITIALIZER;
18
+ archive_data_t* az_proc_queue = NULL;
19
+
20
+ /* Push new callback to front of the queue */
21
+ static void az_proc_queue_push(archive_data_t* adata)
22
+ {
23
+ adata->next = az_proc_queue;
24
+ az_proc_queue = adata;
25
+ }
26
+
27
+ /* Pop next callback from the queue; Returns NULL, when the queue is empty */
28
+ static archive_data_t* az_proc_queue_pop(void)
29
+ {
30
+ archive_data_t* adata = az_proc_queue;
31
+ if(adata)
32
+ {
33
+ az_proc_queue = adata->next;
34
+ }
35
+
36
+ return adata;
37
+ }
38
+
39
+ /* Callback executed by Ruby Thread */
40
+ static VALUE az_handle_proc(void *d)
41
+ {
42
+ archive_data_t* adata = (archive_data_t*)d;
43
+
44
+ // Invoke callback with task argument
45
+ VALUE proc = (VALUE)adata->proc;
46
+
47
+ int is_create = ((adata->zip_path && !adata->dst_path) ? 1 : 0);
48
+
49
+ VALUE task = rb_class_new_instance(0, NULL, cTask);
50
+ az_task_init(task, (is_create ? NULL : adata->zip_path), (is_create ? adata->zip_path : adata->dst_path), adata->err_str, adata->files_arr);
51
+ rb_funcall2(proc, rb_intern("call"), 1, &task);
52
+ rb_gc_unregister_address(&adata->proc);
53
+
54
+ az_free_archive_data(adata);
55
+
56
+ return Qnil;
57
+ }
58
+
59
+
60
+ /* Wait until we have some callbacks to process */
61
+ static VALUE az_wait_for_adata(void* w)
62
+ {
63
+ adata_wait_t* waiter = (adata_wait_t*)w;
64
+
65
+ pthread_mutex_lock(&az_proc_mutex);
66
+ while (!waiter->abort && (waiter->adata = az_proc_queue_pop()) == NULL)
67
+ {
68
+ pthread_cond_wait(&az_proc_cond, &az_proc_mutex);
69
+ }
70
+ pthread_mutex_unlock(&az_proc_mutex);
71
+
72
+ return Qnil;
73
+ }
74
+
75
+ /* Stop waiting for callbacks */
76
+ static void az_stop_waiting_for_adata(void* w)
77
+ {
78
+ adata_wait_t* waiter = (adata_wait_t*)w;
79
+
80
+ pthread_mutex_lock(&az_proc_mutex);
81
+ waiter->abort = 1;
82
+ pthread_mutex_unlock(&az_proc_mutex);
83
+ pthread_cond_signal(&az_proc_cond);
84
+ }
85
+
86
+
87
+ /* ruby event thread, waiting for processed archives to invoke a callback */
88
+ static VALUE az_event_thread(void *unused)
89
+ {
90
+ adata_wait_t waiter = { .adata = NULL, .abort = 0 };
91
+ while (!waiter.abort)
92
+ {
93
+ rb_thread_blocking_region(az_wait_for_adata, &waiter, az_stop_waiting_for_adata, &waiter);
94
+ if (waiter.adata)
95
+ {
96
+ rb_thread_create(az_handle_proc, waiter.adata);
97
+ }
98
+ }
99
+
100
+ return Qnil;
101
+ }
102
+
103
+ /* Initialize Ruby Event Thread for invokation of user-provider callbacks */
104
+ void az_create_event_thread(void)
105
+ {
106
+ rb_thread_create(az_event_thread, NULL);
107
+ }
108
+
109
+ /* Add the archive data to the event queue */
110
+ void az_add_to_event_qeueue(archive_data_t* adata)
111
+ {
112
+ pthread_mutex_lock(&az_proc_mutex);
113
+ az_proc_queue_push(adata);
114
+ pthread_mutex_unlock(&az_proc_mutex);
115
+ pthread_cond_signal(&az_proc_cond);
116
+ }
117
+
118
+ void init_async_zip_event_thread(void)
119
+ {
120
+ az_create_event_thread();
121
+ }