lsqpack 0.1.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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +82 -0
  4. data/LICENSE +21 -0
  5. data/README.md +36 -0
  6. data/Rakefile +19 -0
  7. data/Steepfile +6 -0
  8. data/ext/lsqpack/extconf.rb +18 -0
  9. data/ext/lsqpack/lsqpack.c +426 -0
  10. data/ext/lsqpack/lsqpack.h +6 -0
  11. data/lib/lsqpack/version.rb +5 -0
  12. data/lib/lsqpack.rb +30 -0
  13. data/ls-qpack/.appveyor.yml +14 -0
  14. data/ls-qpack/.cirrus.yml +6 -0
  15. data/ls-qpack/.travis.yml +32 -0
  16. data/ls-qpack/CMakeLists.txt +66 -0
  17. data/ls-qpack/LICENSE +21 -0
  18. data/ls-qpack/README.md +65 -0
  19. data/ls-qpack/bin/CMakeLists.txt +21 -0
  20. data/ls-qpack/bin/encode-int.c +87 -0
  21. data/ls-qpack/bin/fuzz-decode.c +247 -0
  22. data/ls-qpack/bin/interop-decode.c +433 -0
  23. data/ls-qpack/bin/interop-encode.c +554 -0
  24. data/ls-qpack/deps/xxhash/xxhash.c +941 -0
  25. data/ls-qpack/deps/xxhash/xxhash.h +160 -0
  26. data/ls-qpack/fuzz/decode/a/README +3 -0
  27. data/ls-qpack/fuzz/decode/a/preamble +0 -0
  28. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,sig_06,src_000390,op_havoc,rep_4 +0 -0
  29. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,sig_06,src_000579,op_flip1,pos_14 +0 -0
  30. data/ls-qpack/fuzz/decode/a/test-cases/id_000000,src_000000,op_flip2,pos_12 +0 -0
  31. data/ls-qpack/fuzz/decode/a/test-cases/id_000001,sig_11,src_000579,op_havoc,rep_4 +0 -0
  32. data/ls-qpack/fuzz/decode/a/test-cases/id_000002,sig_11,src_000481,op_int16,pos_15,val_-1 +0 -0
  33. data/ls-qpack/fuzz/decode/a/test-cases/id_000002,src_000000,op_havoc,rep_8 +0 -0
  34. data/ls-qpack/fuzz/decode/a/test-cases/id_000006,src_000285,op_flip2,pos_14 +0 -0
  35. data/ls-qpack/fuzz/decode/a/test-cases/id_000008,src_000285,op_flip2,pos_20 +0 -0
  36. data/ls-qpack/fuzz/decode/a/test-cases/id_000010,src_000306,op_flip2,pos_75 +0 -0
  37. data/ls-qpack/fuzz/decode/a/test-cases/id_000011,src_000344,op_havoc,rep_2 +0 -0
  38. data/ls-qpack/fuzz/decode/a/test-cases/id_000014,src_000366,op_flip2,pos_28 +0 -0
  39. data/ls-qpack/fuzz/decode/b/README +1 -0
  40. data/ls-qpack/fuzz/decode/b/preamble +0 -0
  41. data/ls-qpack/fuzz/decode/b/test-cases/seed +0 -0
  42. data/ls-qpack/fuzz/decode/c/setup.sh +3 -0
  43. data/ls-qpack/fuzz/decode/c/test-cases/fb-req.qif.proxygen.out.256.100.0-chopped +0 -0
  44. data/ls-qpack/fuzz/decode/d/preamble +0 -0
  45. data/ls-qpack/fuzz/decode/d/setup.sh +3 -0
  46. data/ls-qpack/fuzz/decode/d/test-cases/fb-resp.minhq.256.128.0.ack +0 -0
  47. data/ls-qpack/fuzz/input/256.100.1/fb-req.out.256.100.1 +0 -0
  48. data/ls-qpack/fuzz/input/256.100.1/fb-resp.out.256.100.1 +0 -0
  49. data/ls-qpack/fuzz/input/256.100.1/netbsd.out.256.100.1 +0 -0
  50. data/ls-qpack/huff-tables.h +136247 -0
  51. data/ls-qpack/lsqpack.c +5547 -0
  52. data/ls-qpack/lsqpack.h +768 -0
  53. data/ls-qpack/test/CMakeLists.txt +76 -0
  54. data/ls-qpack/test/lsqpack-test.h +43 -0
  55. data/ls-qpack/test/qifs/fb-req.qif +4917 -0
  56. data/ls-qpack/test/qifs/fb-resp.qif +5982 -0
  57. data/ls-qpack/test/qifs/long-codes.qif +5984 -0
  58. data/ls-qpack/test/qifs/netbsd.qif +235 -0
  59. data/ls-qpack/test/run-qif.pl +97 -0
  60. data/ls-qpack/test/run-scenario.sh +68 -0
  61. data/ls-qpack/test/scenarios/0.95-reset.sce +10 -0
  62. data/ls-qpack/test/scenarios/cancel-stream.sce +22 -0
  63. data/ls-qpack/test/scenarios/drain-2.sce +37 -0
  64. data/ls-qpack/test/scenarios/drain.sce +37 -0
  65. data/ls-qpack/test/scenarios/end-dst-2.sce +14 -0
  66. data/ls-qpack/test/scenarios/end-dst.sce +14 -0
  67. data/ls-qpack/test/scenarios/incl-name.sce +13 -0
  68. data/ls-qpack/test/scenarios/multi-byte-int-dyn-ref-1.sce +110 -0
  69. data/ls-qpack/test/scenarios/multi-byte-int-dyn-ref-2.sce +161 -0
  70. data/ls-qpack/test/scenarios/post-base-1.sce +10 -0
  71. data/ls-qpack/test/scenarios/post-base-2.sce +13 -0
  72. data/ls-qpack/test/scenarios/post-base-nr.sce +10 -0
  73. data/ls-qpack/test/scenarios/set-max-cap.sce +15 -0
  74. data/ls-qpack/test/test_enc_str.c +139 -0
  75. data/ls-qpack/test/test_get_stx_id.c +144 -0
  76. data/ls-qpack/test/test_huff_dec.c +399 -0
  77. data/ls-qpack/test/test_int.c +220 -0
  78. data/ls-qpack/test/test_qpack.c +856 -0
  79. data/ls-qpack/test/test_read_enc_stream.c +256 -0
  80. data/ls-qpack/tools/har2qif.pl +139 -0
  81. data/ls-qpack/tools/randomize-cookies.pl +41 -0
  82. data/ls-qpack/tools/sort-qif.pl +31 -0
  83. data/ls-qpack/wincompat/getopt.c +758 -0
  84. data/ls-qpack/wincompat/getopt.h +131 -0
  85. data/ls-qpack/wincompat/getopt1.c +188 -0
  86. data/ls-qpack/wincompat/sys/queue.h +859 -0
  87. data/lsqpack.gemspec +39 -0
  88. data/sig/lsqpack.rbs +29 -0
  89. metadata +135 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3817dceb640976607a1f80f70359dff77831721822c80bf9055b90bd3fb51e5
4
+ data.tar.gz: 057c391779387b76cfec1e07141fab5e75e713af5f26fa6310899a6dc8d046d6
5
+ SHA512:
6
+ metadata.gz: 2f6bec1a935ce51d1568eb83673cb14556cd82f437b219e7a7c1f23cbcbe948a123507ac5a5cb71b2af2d92395430eca3c37702c889f13e8a2edb1e3acca7206
7
+ data.tar.gz: 8c6f3f732c047544e8d5e41ec956832028e5f2bab18e044c0ba0620f91bfd9813be10b442493ae1e87dcdf80af968e59f7005430fffdbc311e72777b086afb53
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in lsqpack.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rake-compiler"
11
+
12
+ group :development do
13
+ gem "steep"
14
+ end
15
+
16
+ group :test do
17
+ gem "minitest"
18
+ gem "minitest-reporters"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lsqpack (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (7.0.4.3)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 1.6, < 2)
12
+ minitest (>= 5.1)
13
+ tzinfo (~> 2.0)
14
+ ansi (1.5.0)
15
+ ast (2.4.2)
16
+ builder (3.2.4)
17
+ concurrent-ruby (1.2.2)
18
+ csv (3.2.6)
19
+ ffi (1.15.5)
20
+ fileutils (1.7.0)
21
+ i18n (1.12.0)
22
+ concurrent-ruby (~> 1.0)
23
+ json (2.6.3)
24
+ language_server-protocol (3.17.0.3)
25
+ listen (3.8.0)
26
+ rb-fsevent (~> 0.10, >= 0.10.3)
27
+ rb-inotify (~> 0.9, >= 0.9.10)
28
+ logger (1.5.3)
29
+ minitest (5.18.0)
30
+ minitest-reporters (1.6.0)
31
+ ansi
32
+ builder
33
+ minitest (>= 5.0)
34
+ ruby-progressbar
35
+ parallel (1.22.1)
36
+ parser (3.2.1.1)
37
+ ast (~> 2.4.1)
38
+ rainbow (3.1.1)
39
+ rake (13.0.6)
40
+ rake-compiler (1.2.0)
41
+ rake
42
+ rb-fsevent (0.11.2)
43
+ rb-inotify (0.10.1)
44
+ ffi (~> 1.0)
45
+ rbs (2.8.4)
46
+ ruby-progressbar (1.13.0)
47
+ securerandom (0.2.2)
48
+ steep (1.3.2)
49
+ activesupport (>= 5.1)
50
+ csv (>= 3.0.9)
51
+ fileutils (>= 1.1.0)
52
+ json (>= 2.1.0)
53
+ language_server-protocol (>= 3.15, < 4.0)
54
+ listen (~> 3.0)
55
+ logger (>= 1.3.0)
56
+ parallel (>= 1.0.0)
57
+ parser (>= 3.1)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ rbs (~> 2.8.0)
60
+ securerandom (>= 0.1)
61
+ strscan (>= 1.0.0)
62
+ terminal-table (>= 2, < 4)
63
+ strscan (3.0.6)
64
+ terminal-table (3.0.2)
65
+ unicode-display_width (>= 1.1.1, < 3)
66
+ tzinfo (2.0.6)
67
+ concurrent-ruby (~> 1.0)
68
+ unicode-display_width (2.4.2)
69
+
70
+ PLATFORMS
71
+ x86_64-linux
72
+
73
+ DEPENDENCIES
74
+ lsqpack!
75
+ minitest
76
+ minitest-reporters
77
+ rake (~> 13.0)
78
+ rake-compiler
79
+ steep
80
+
81
+ BUNDLED WITH
82
+ 2.3.26
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Yusuke Nakamura
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Lsqpack
2
+
3
+ lsqpack-ruby is a wrapper gem of the [litespeedtech/ls-qpack](https://github.com/litespeedtech/ls-qpack) library. It strongly influenced by [pylsqpack](https://github.com/aiortc/pylsqpack).
4
+ It strongly influenced by pylsqpack, so it has same api of pylsqpack.
5
+
6
+ ## Installation
7
+
8
+ Install the gem and add to the application's Gemfile by executing:
9
+
10
+ ```sh
11
+ $ bundle add lsqpack
12
+ ```
13
+
14
+ If bundler is not being used to manage dependencies, install the gem by executing:
15
+
16
+ ```sh
17
+ $ gem install lsqpack
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ See also <https://pylsqpack.readthedocs.io/en/latest/>
23
+
24
+ ## Development
25
+
26
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
27
+
28
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
29
+
30
+ ## Contributing
31
+
32
+ Bug reports and pull requests are welcome on GitHub at https://github.com/unasuke/lsqpack-ruby.
33
+
34
+ ## Acknowledgement
35
+ * <https://github.com/litespeedtech/ls-qpack>
36
+ * <https://github.com/aiortc/pylsqpack>
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/extensiontask"
5
+ require "rake/testtask"
6
+
7
+ task build: :compile
8
+
9
+ Rake::ExtensionTask.new("lsqpack") do |ext|
10
+ ext.lib_dir = "lib/lsqpack"
11
+ end
12
+
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << "test"
15
+ t.libs << "lib"
16
+ t.test_files = FileList["test/**/test_*.rb"]
17
+ end
18
+
19
+ task default: %i[clobber compile test]
data/Steepfile ADDED
@@ -0,0 +1,6 @@
1
+ # frosen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig"
5
+ check "lib"
6
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ repo_root = File.join(File.expand_path(__dir__), "../", "../")
6
+
7
+ $CPPFLAGS = "-I #{repo_root}"
8
+ $LDFLAGS = "-lls-qpack -L#{File.join(repo_root, "ls-qpack")}"
9
+
10
+ Dir.chdir(repo_root) do
11
+ Dir.chdir("ls-qpack") do
12
+ puts(system("git status"))
13
+ system("cmake .", exception: true)
14
+ system("make", exception: true)
15
+ end
16
+ end
17
+
18
+ create_makefile("lsqpack/lsqpack")
@@ -0,0 +1,426 @@
1
+ #include "lsqpack.h"
2
+ #include "ls-qpack/lsqpack.h"
3
+ #include <stdio.h>
4
+
5
+ #define HEADER_BUF_SIZE 4096
6
+ #define ENCODER_BUF_SIZE 4096
7
+ #define DECODER_BUF_SIZE 4096
8
+ #define PREFIX_MAX_SIZE 16
9
+
10
+ VALUE rb_mLsqpack;
11
+ VALUE rb_clsEncoder;
12
+ VALUE rb_clsDecoder;
13
+ VALUE rb_eError;
14
+ VALUE rb_eDecompressionFailed;
15
+ VALUE rb_eDecoderStreamErr;
16
+ VALUE rb_eEncoderStreamErr;
17
+ VALUE rb_eStreamBlocked;
18
+
19
+ struct header_block {
20
+ STAILQ_ENTRY(header_block) entries;
21
+ int blocked:1;
22
+ unsigned char* data;
23
+ size_t data_len;
24
+ const unsigned char* data_ptr;
25
+ struct lsqpack_header_list *hlist;
26
+ uint64_t stream_id;
27
+ };
28
+
29
+ static struct header_block *header_block_new(size_t stream_id, const unsigned char* data, size_t data_len) {
30
+ struct header_block* hblock = malloc(sizeof(struct header_block));
31
+ memset(hblock, 0, sizeof(*hblock));
32
+ hblock->data = malloc(data_len);
33
+ hblock->data_len = data_len;
34
+ hblock->data_ptr = hblock->data;
35
+ memcpy(hblock->data, data, data_len);
36
+ hblock->stream_id = stream_id;
37
+ return hblock;
38
+ }
39
+
40
+ static void header_block_free(struct header_block* hblock) {
41
+ free(hblock->data);
42
+ hblock->data = 0;
43
+ hblock->data_ptr = 0;
44
+ if(hblock->hlist) {
45
+ lsqpack_dec_destroy_header_list(hblock->hlist);
46
+ hblock->hlist = 0;
47
+ }
48
+ free(hblock);
49
+ }
50
+
51
+ VALUE hlist_to_headers(struct lsqpack_header_list* hlist) {
52
+ VALUE list, tuple, name, value;
53
+ struct lsqpack_header* header;
54
+
55
+ list = rb_ary_new_capa(hlist->qhl_count);
56
+ for (size_t i = 0; i < hlist->qhl_count; i++) {
57
+ header = hlist->qhl_headers[i];
58
+ name = rb_str_new(header->qh_name, header->qh_name_len);
59
+ value = rb_str_new(header->qh_value, header->qh_value_len);
60
+ tuple = rb_ary_new_capa(2);
61
+ rb_ary_store(tuple, 0, name);
62
+ rb_ary_store(tuple, 1, value);
63
+ rb_ary_store(list, i, tuple);
64
+ }
65
+ return list;
66
+ }
67
+
68
+ static void header_unblocked(void* opaque) {
69
+ struct header_block* hblock = opaque;
70
+ hblock->blocked = 0;
71
+ }
72
+
73
+ typedef struct {
74
+ struct lsqpack_dec dec;
75
+ unsigned char decoder_buf[DECODER_BUF_SIZE];
76
+ STAILQ_HEAD(, header_block) pending_blocks;
77
+ } DecoderObj;
78
+
79
+ size_t lsqpackrb_dec_size(DecoderObj* data) {
80
+ return sizeof(DecoderObj); // Is it correct?
81
+ }
82
+
83
+ void lsqpackrb_dec_free(DecoderObj* data) {
84
+ struct header_block* hblock;
85
+ lsqpack_dec_cleanup(&data->dec);
86
+ while (!STAILQ_EMPTY(&data->pending_blocks)) {
87
+ hblock = STAILQ_FIRST(&data->pending_blocks);
88
+ STAILQ_REMOVE_HEAD(&data->pending_blocks, entries);
89
+ header_block_free(hblock);
90
+ }
91
+ free(data);
92
+ }
93
+
94
+ static const rb_data_type_t decoder_data_type = {
95
+ .wrap_struct_name = "lsqpack_dec_rb",
96
+ .function = {
97
+ .dmark = NULL,
98
+ .dfree = lsqpackrb_dec_free,
99
+ .dsize = lsqpackrb_dec_size,
100
+ },
101
+ .data = NULL,
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
103
+ };
104
+
105
+ VALUE lsqpackrb_dec_alloc(VALUE self) {
106
+ DecoderObj* data = malloc(sizeof(DecoderObj));
107
+ return TypedData_Wrap_Struct(self, &decoder_data_type, data);
108
+ }
109
+
110
+
111
+ VALUE lsqpackrb_dec_init(int argc, VALUE *argv, VALUE self) {
112
+ DecoderObj* data;
113
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, data);
114
+
115
+ VALUE hash;
116
+ hash = rb_check_hash_type(argv[argc-1]);
117
+ unsigned int max_tbl_capa, blked_streams;
118
+ ID table[2];
119
+ VALUE values[2];
120
+ table[0] = rb_intern("max_table_capacity");
121
+ table[1] = rb_intern("blocked_streams");
122
+ rb_get_kwargs(hash, table, 0, 2, values);
123
+ max_tbl_capa = (values[0] != Qundef) ? (FIX2INT(values[0])) : 0;
124
+ blked_streams = (values[1] != Qundef) ? (FIX2INT(values[1])) : 0;
125
+
126
+ lsqpack_dec_init(&data->dec, NULL, max_tbl_capa, blked_streams, header_unblocked);
127
+ STAILQ_INIT(&data->pending_blocks);
128
+ return self;
129
+ }
130
+
131
+
132
+ VALUE lsqpackrb_dec_feed_encoder(VALUE self, VALUE data) {
133
+ DecoderObj* dec;
134
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
135
+ struct header_block* hblock;
136
+ Check_Type(data, T_STRING);
137
+ unsigned char* feed_data;
138
+ feed_data = StringValuePtr(data);
139
+ size_t feed_data_len;
140
+ feed_data_len = RSTRING_LEN(data);
141
+
142
+ if (lsqpack_dec_enc_in(&dec->dec, feed_data, feed_data_len) < 0) {
143
+ rb_raise(rb_eEncoderStreamErr, "lsqpack_dec_enc_in failed");
144
+ return NULL;
145
+ }
146
+
147
+ VALUE list = rb_ary_new();
148
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
149
+ if(!hblock->blocked) {
150
+ VALUE val = LONG2NUM(hblock->stream_id);
151
+ rb_ary_push(list, val);
152
+ }
153
+ }
154
+ return list;
155
+ }
156
+
157
+ VALUE lsqpackrb_dec_feed_header(VALUE self, VALUE stream_id, VALUE data) {
158
+ DecoderObj* dec;
159
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
160
+ uint64_t strm_id;
161
+ size_t dec_len = DECODER_BUF_SIZE;
162
+ unsigned char* feed_data;
163
+ int feed_data_len;
164
+ enum lsqpack_read_header_status status;
165
+ struct header_block* hblock;
166
+
167
+ strm_id = FIX2LONG(stream_id);
168
+ Check_Type(data, T_STRING);
169
+ feed_data = StringValuePtr(data);
170
+ feed_data_len = RSTRING_LEN(data);
171
+
172
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
173
+ if (hblock->stream_id == strm_id) {
174
+ rb_raise(rb_eError, "a header block for stream %d already exists", strm_id);
175
+ return NULL;
176
+ }
177
+ }
178
+ hblock = header_block_new(strm_id, feed_data, feed_data_len);
179
+ status = lsqpack_dec_header_in(
180
+ &dec->dec,
181
+ hblock,
182
+ strm_id,
183
+ hblock->data_len,
184
+ &hblock->data_ptr,
185
+ hblock->data_len,
186
+ &hblock->hlist,
187
+ dec->decoder_buf,
188
+ &dec_len
189
+ );
190
+ if (status == LQRHS_BLOCKED || status == LQRHS_NEED) {
191
+ hblock->blocked = 1;
192
+ STAILQ_INSERT_TAIL(&dec->pending_blocks, hblock, entries);
193
+ rb_raise(rb_eStreamBlocked, "stream %d is blocked", strm_id);
194
+ return NULL;
195
+ } else if (status != LQRHS_DONE) {
196
+ rb_raise(rb_eDecompressionFailed, "lsqpack_dec_header_in for stream %d failed", strm_id);
197
+ header_block_free(hblock);
198
+ return NULL;
199
+ }
200
+
201
+ VALUE control = rb_str_new(dec->decoder_buf, dec_len);
202
+ VALUE headers = hlist_to_headers(hblock->hlist);
203
+ header_block_free(hblock);
204
+ VALUE tuple = rb_ary_new_capa(2);
205
+ rb_ary_store(tuple, 0, control);
206
+ rb_ary_store(tuple, 1, headers);
207
+ return tuple;
208
+ }
209
+
210
+ VALUE lsqpackrb_dec_resume_header(VALUE self, VALUE stream_id) {
211
+ DecoderObj* dec;
212
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, dec);
213
+ uint64_t strm_id;
214
+ size_t dec_len = DECODER_BUF_SIZE;
215
+ enum lsqpack_read_header_status status;
216
+ struct header_block *hblock;
217
+ int found = 0;
218
+
219
+ Check_Type(stream_id, T_FIXNUM);
220
+ strm_id = NUM2LONG(stream_id);
221
+
222
+ STAILQ_FOREACH(hblock, &dec->pending_blocks, entries) {
223
+ if(hblock->stream_id == strm_id) {
224
+ found = 1;
225
+ break;
226
+ }
227
+ }
228
+ if (!found) {
229
+ rb_raise(rb_eError, "no pending header block for stream %d", strm_id);
230
+ return NULL;
231
+ }
232
+ if (hblock->blocked) {
233
+ status = LQRHS_BLOCKED;
234
+ } else {
235
+ status = lsqpack_dec_header_read(
236
+ &dec->dec,
237
+ hblock,
238
+ &hblock->data_ptr,
239
+ hblock->data_len - (hblock->data_ptr - hblock->data),
240
+ &hblock->hlist,
241
+ dec->decoder_buf,
242
+ &dec_len
243
+ );
244
+ }
245
+
246
+ if (status == LQRHS_BLOCKED || status == LQRHS_NEED) {
247
+ hblock->blocked = 1;
248
+ rb_raise(rb_eStreamBlocked, "stream %d is blocked", strm_id);
249
+ return NULL;
250
+ } else if (status != LQRHS_DONE) {
251
+ rb_raise(rb_eDecompressionFailed, "lsqpack_dec_header_in for stream %d failed (%d)", strm_id, status);
252
+ STAILQ_REMOVE(&dec->pending_blocks, hblock, header_block, entries);
253
+ header_block_free(hblock);
254
+ return NULL;
255
+ }
256
+ VALUE control = rb_str_new(dec->decoder_buf, dec_len);
257
+ VALUE headers = hlist_to_headers(hblock->hlist);
258
+ VALUE tuple = rb_ary_new_capa(2);
259
+ rb_ary_store(tuple, 0, control);
260
+ rb_ary_store(tuple, 1, headers);
261
+ return tuple;
262
+ }
263
+
264
+
265
+
266
+ typedef struct {
267
+ struct lsqpack_enc enc;
268
+ unsigned char header_buf[HEADER_BUF_SIZE];
269
+ unsigned char encoder_buf[ENCODER_BUF_SIZE];
270
+ unsigned char prefix_buf[PREFIX_MAX_SIZE];
271
+ } EncoderObj;
272
+
273
+ size_t lsqpackrb_enc_size(EncoderObj* data) {
274
+ return sizeof(EncoderObj);
275
+ }
276
+
277
+ void lsqpackrb_enc_free(EncoderObj* data) {
278
+ lsqpack_enc_cleanup(&data->enc);
279
+ free(data);
280
+ }
281
+
282
+ static const rb_data_type_t encoder_data_type = {
283
+ .wrap_struct_name = "lsqpack_enc_rb",
284
+ .function = {
285
+ .dmark = NULL,
286
+ .dfree = lsqpackrb_enc_free,
287
+ .dsize = lsqpackrb_enc_size,
288
+ },
289
+ .data = NULL,
290
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
291
+ };
292
+
293
+ VALUE lsqpackrb_enc_alloc(VALUE self) {
294
+ EncoderObj* data = malloc(sizeof(EncoderObj));
295
+ return TypedData_Wrap_Struct(self, &encoder_data_type, data);
296
+ }
297
+
298
+ VALUE lsqpackrb_enc_init(VALUE self) {
299
+ EncoderObj* data;
300
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
301
+ lsqpack_enc_preinit(&data->enc, NULL);
302
+ return self;
303
+ }
304
+
305
+ VALUE lsqpackrb_enc_apply_settings(VALUE self, VALUE rb_max_table_capacity, VALUE rb_blocked_streams) {
306
+ EncoderObj* data;
307
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
308
+
309
+ unsigned int max_table_capacity = FIX2INT(rb_max_table_capacity);
310
+ unsigned int blocked_streams = FIX2INT(rb_blocked_streams);
311
+ unsigned char stdc_buf[LSQPACK_LONGEST_SDTC];
312
+ size_t stdc_buf_len = sizeof(stdc_buf);
313
+
314
+ if(lsqpack_enc_init(&data->enc, NULL, max_table_capacity, max_table_capacity, blocked_streams, LSQPACK_ENC_OPT_STAGE_2, stdc_buf, &stdc_buf_len) != 0) {
315
+ rb_raise(rb_eError, "lsqpack_enc_init failed");
316
+ return NULL;
317
+ }
318
+ return rb_str_new(stdc_buf, stdc_buf_len);
319
+ }
320
+
321
+ VALUE lsqpackrb_enc_encode(VALUE self, VALUE stream_id, VALUE headers) {
322
+ EncoderObj* data;
323
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
324
+
325
+ uint64_t str_id = FIX2LONG(stream_id);
326
+ unsigned int sequence_number = 0;
327
+ size_t enc_len, header_len, prefix_len;
328
+ size_t enc_off = 0, header_off = PREFIX_MAX_SIZE, prefix_off = 0;
329
+
330
+ Check_Type(headers, T_ARRAY);
331
+ if (rb_array_len(headers) == 0) {
332
+ // no headers present
333
+ return NULL;
334
+ }
335
+
336
+ if (lsqpack_enc_start_header(&data->enc, str_id, sequence_number) != 0) {
337
+ rb_raise(rb_eError, "lsqpack_enc_start_header failed");
338
+ return NULL;
339
+ }
340
+
341
+ for (int i = 0; i < rb_array_len(headers); i++) {
342
+ VALUE entry = rb_ary_entry(headers, i);
343
+ if (rb_array_len(entry) != 2) {
344
+ // no headers present
345
+ rb_raise(rb_eError, "Each header entry must be include 2 elements");
346
+ return NULL;
347
+ }
348
+
349
+ VALUE name_ = rb_ary_entry(entry, 0);
350
+ VALUE value_ = rb_ary_entry(entry, 1);
351
+ char* name = StringValuePtr(name_);
352
+ int name_len = RSTRING_LEN(name_);
353
+ char* value = StringValuePtr(value_);
354
+ int value_len = RSTRING_LEN(value_);
355
+
356
+ enc_len = ENCODER_BUF_SIZE - enc_off;
357
+ header_len = HEADER_BUF_SIZE - header_off;
358
+ // printf("==========name %s, len %d\n", name, name_len);
359
+ // printf("==========value %s, len %d\n", value, value_len);
360
+ // printf("==========enc_len(init) %d\n", enc_len);
361
+ if (lsqpack_enc_encode(&data->enc,
362
+ data->encoder_buf + enc_off, &enc_len,
363
+ data->header_buf + header_off, &header_len,
364
+ name, name_len, value, value_len, 0) != LQES_OK) {
365
+ rb_raise(rb_eError, "lsqpack_enc_encode failed");
366
+ return NULL;
367
+ }
368
+ // printf("==========enc_len %d\n", enc_len);
369
+ enc_off += enc_len;
370
+ header_off += header_len;
371
+ // printf("==========enc_off %d\n", enc_off);
372
+ }
373
+
374
+ prefix_len = lsqpack_enc_end_header(&data->enc, data->prefix_buf, PREFIX_MAX_SIZE, NULL);
375
+ if (prefix_len <= 0) {
376
+ rb_raise(rb_eError, "lsqpack_enc_end_header failed");
377
+ return NULL;
378
+ }
379
+ prefix_off = PREFIX_MAX_SIZE - prefix_len;
380
+ memcpy(data->header_buf + prefix_off, data->prefix_buf, prefix_len);
381
+ VALUE name = rb_str_new(data->encoder_buf, enc_off);
382
+ VALUE value = rb_str_new(data->header_buf + prefix_off, (int)(header_off - prefix_off));
383
+ VALUE tuple = rb_ary_new_from_args(2, name, value);
384
+ return tuple;
385
+ }
386
+
387
+ void lsqpackrb_enc_feed_decoder(VALUE self, VALUE rb_data) {
388
+ EncoderObj* data;
389
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, data);
390
+ char* feed_data = StringValuePtr(rb_data);
391
+ int feed_data_len = RSTRING_LEN(rb_data);
392
+
393
+ if (lsqpack_enc_decoder_in(&data->enc, feed_data, feed_data_len) < 0) {
394
+ rb_raise(rb_eDecoderStreamErr, "lsqpack_enc_decoder_in failed");
395
+ return NULL;
396
+ }
397
+ return NULL;
398
+ }
399
+
400
+
401
+ void
402
+ Init_lsqpack(void)
403
+ {
404
+ rb_mLsqpack = rb_define_module("Lsqpack");
405
+ rb_eError = rb_define_class_under(rb_mLsqpack, "Error", rb_eStandardError);
406
+ rb_eDecompressionFailed = rb_define_class_under(rb_mLsqpack, "DecompressionFailed", rb_eError);
407
+ rb_eDecoderStreamErr = rb_define_class_under(rb_mLsqpack, "DecoderStreamError", rb_eError);
408
+ rb_eEncoderStreamErr = rb_define_class_under(rb_mLsqpack, "EncoderStreamError", rb_eError);
409
+ rb_eStreamBlocked = rb_define_class_under(rb_mLsqpack, "StreamBlocked", rb_eError);
410
+
411
+ // Encoder
412
+ rb_clsEncoder = rb_define_class_under(rb_mLsqpack, "Encoder", rb_cObject);
413
+ rb_define_alloc_func(rb_clsEncoder, lsqpackrb_enc_alloc);
414
+ rb_define_method(rb_clsEncoder, "initialize", lsqpackrb_enc_init, 0);
415
+ rb_define_private_method(rb_clsEncoder, "_apply_settings", lsqpackrb_enc_apply_settings, 2);
416
+ rb_define_method(rb_clsEncoder, "encode", lsqpackrb_enc_encode, 2);
417
+ rb_define_method(rb_clsEncoder, "feed_decoder", lsqpackrb_enc_feed_decoder, 1);
418
+
419
+ // Decoder
420
+ rb_clsDecoder = rb_define_class_under(rb_mLsqpack, "Decoder", rb_cObject);
421
+ rb_define_alloc_func(rb_clsDecoder, lsqpackrb_dec_alloc);
422
+ rb_define_method(rb_clsDecoder, "initialize", lsqpackrb_dec_init, -1);
423
+ rb_define_method(rb_clsDecoder, "feed_encoder", lsqpackrb_dec_feed_encoder, 1);
424
+ rb_define_method(rb_clsDecoder, "feed_header", lsqpackrb_dec_feed_header, 2);
425
+ rb_define_method(rb_clsDecoder, "resume_header", lsqpackrb_dec_resume_header, 1);
426
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef LSQPACK_RB_H
2
+ #define LSQPACK_RB_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ #endif /* LSQPACK_RB_H */
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lsqpack
4
+ VERSION = "0.1.0"
5
+ end
data/lib/lsqpack.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lsqpack/version"
4
+ require_relative "lsqpack/lsqpack"
5
+
6
+ module Lsqpack
7
+ # class Error < StandardError; end
8
+ # class DecompressonFailed < Error; end
9
+ # class DecoderStreamError < Error; end
10
+ # class EncoderStreamError < Error; end
11
+ # class StreamBlocked < Error; end
12
+ # Your code goes here...
13
+
14
+ class Encoder
15
+ # def initialize
16
+ # end
17
+
18
+ def apply_settings(max_table_capacity: 0, blocked_streams: 0)
19
+ _apply_settings(max_table_capacity, blocked_streams)
20
+ end
21
+ end
22
+
23
+ # class Decoder
24
+ # def initialize
25
+ # end
26
+
27
+ # def decode
28
+ # end
29
+ # end
30
+ end
@@ -0,0 +1,14 @@
1
+ version: 1.0.{build}
2
+
3
+ before_build:
4
+ - cmake -DLSQPACK_TESTS=ON .
5
+
6
+ build_script:
7
+ - cmake --build .
8
+
9
+ before_test:
10
+ - set PATH=%PATH%;c:\projects\ls-qpack\bin\Debug
11
+
12
+ test_script:
13
+ - dir
14
+ - msbuild RUN_TESTS.vcxproj