async-ruby-zip 1.0.0

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.
@@ -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
+ }