nghttp3 0.0.1

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,680 @@
1
+ #include "nghttp3.h"
2
+
3
+ VALUE rb_mNghttp3QPACK;
4
+ VALUE rb_cNghttp3QPACKEncoder;
5
+ VALUE rb_cNghttp3QPACKDecoder;
6
+
7
+ /* ============== Encoder ============== */
8
+
9
+ typedef struct {
10
+ nghttp3_qpack_encoder *encoder;
11
+ size_t hard_max_dtable_capacity;
12
+ } EncoderObj;
13
+
14
+ static void encoder_free(void *ptr) {
15
+ EncoderObj *obj = (EncoderObj *)ptr;
16
+ if (obj->encoder != NULL) {
17
+ nghttp3_qpack_encoder_del(obj->encoder);
18
+ obj->encoder = NULL;
19
+ }
20
+ xfree(ptr);
21
+ }
22
+
23
+ static size_t encoder_memsize(const void *ptr) { return sizeof(EncoderObj); }
24
+
25
+ static const rb_data_type_t encoder_data_type = {
26
+ .wrap_struct_name = "nghttp3_qpack_encoder_rb",
27
+ .function =
28
+ {
29
+ .dmark = NULL,
30
+ .dfree = encoder_free,
31
+ .dsize = encoder_memsize,
32
+ },
33
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
34
+ };
35
+
36
+ static VALUE encoder_alloc(VALUE klass) {
37
+ EncoderObj *obj;
38
+ VALUE self =
39
+ TypedData_Make_Struct(klass, EncoderObj, &encoder_data_type, obj);
40
+ obj->encoder = NULL;
41
+ obj->hard_max_dtable_capacity = 0;
42
+ return self;
43
+ }
44
+
45
+ /*
46
+ * call-seq:
47
+ * Encoder.new(max_dtable_capacity) -> Encoder
48
+ *
49
+ * Creates a new QPACK encoder.
50
+ */
51
+ static VALUE rb_nghttp3_qpack_encoder_initialize(VALUE self,
52
+ VALUE rb_max_capacity) {
53
+ EncoderObj *obj;
54
+ size_t max_capacity;
55
+ int rv;
56
+
57
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
58
+
59
+ max_capacity = NUM2SIZET(rb_max_capacity);
60
+ obj->hard_max_dtable_capacity = max_capacity;
61
+
62
+ rv = nghttp3_qpack_encoder_new(&obj->encoder, max_capacity,
63
+ nghttp3_mem_default());
64
+
65
+ if (rv != 0) {
66
+ nghttp3_rb_raise(rv, "Failed to create QPACK encoder");
67
+ }
68
+
69
+ return self;
70
+ }
71
+
72
+ /*
73
+ * Helper to convert nghttp3_buf to Ruby String
74
+ */
75
+ static VALUE buf_to_string(nghttp3_buf *buf) {
76
+ size_t len = nghttp3_buf_len(buf);
77
+ if (len == 0) {
78
+ return rb_str_new("", 0);
79
+ }
80
+ return rb_str_new((const char *)buf->pos, len);
81
+ }
82
+
83
+ /*
84
+ * call-seq:
85
+ * encoder.encode(stream_id, headers) -> Hash
86
+ *
87
+ * Encodes headers. Returns a Hash with :prefix, :data, and :encoder_stream.
88
+ */
89
+ static VALUE rb_nghttp3_qpack_encoder_encode(VALUE self, VALUE rb_stream_id,
90
+ VALUE rb_headers) {
91
+ EncoderObj *obj;
92
+ int64_t stream_id;
93
+ nghttp3_nv *nva;
94
+ size_t nvlen, i;
95
+ nghttp3_buf pbuf, rbuf, ebuf;
96
+ int rv;
97
+ VALUE result;
98
+
99
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
100
+
101
+ if (obj->encoder == NULL) {
102
+ rb_raise(rb_eNghttp3InvalidStateError, "Encoder is not initialized");
103
+ }
104
+
105
+ stream_id = NUM2LL(rb_stream_id);
106
+ Check_Type(rb_headers, T_ARRAY);
107
+ nvlen = RARRAY_LEN(rb_headers);
108
+
109
+ /* Allocate and convert headers */
110
+ nva = ALLOCA_N(nghttp3_nv, nvlen);
111
+ for (i = 0; i < nvlen; i++) {
112
+ VALUE rb_nv = rb_ary_entry(rb_headers, i);
113
+ nva[i] = nghttp3_rb_nv_to_c(rb_nv);
114
+ }
115
+
116
+ /* Initialize buffers */
117
+ nghttp3_buf_init(&pbuf);
118
+ nghttp3_buf_init(&rbuf);
119
+ nghttp3_buf_init(&ebuf);
120
+
121
+ rv = nghttp3_qpack_encoder_encode(obj->encoder, &pbuf, &rbuf, &ebuf,
122
+ stream_id, nva, nvlen);
123
+
124
+ if (rv != 0) {
125
+ nghttp3_buf_free(&pbuf, nghttp3_mem_default());
126
+ nghttp3_buf_free(&rbuf, nghttp3_mem_default());
127
+ nghttp3_buf_free(&ebuf, nghttp3_mem_default());
128
+ nghttp3_rb_raise(rv, "Failed to encode headers");
129
+ }
130
+
131
+ result = rb_hash_new();
132
+ rb_hash_aset(result, ID2SYM(rb_intern("prefix")), buf_to_string(&pbuf));
133
+ rb_hash_aset(result, ID2SYM(rb_intern("data")), buf_to_string(&rbuf));
134
+ rb_hash_aset(result, ID2SYM(rb_intern("encoder_stream")),
135
+ buf_to_string(&ebuf));
136
+
137
+ nghttp3_buf_free(&pbuf, nghttp3_mem_default());
138
+ nghttp3_buf_free(&rbuf, nghttp3_mem_default());
139
+ nghttp3_buf_free(&ebuf, nghttp3_mem_default());
140
+
141
+ return result;
142
+ }
143
+
144
+ /*
145
+ * call-seq:
146
+ * encoder.read_decoder(data) -> Integer
147
+ *
148
+ * Reads decoder stream data. Returns number of bytes consumed.
149
+ */
150
+ static VALUE rb_nghttp3_qpack_encoder_read_decoder(VALUE self, VALUE rb_data) {
151
+ EncoderObj *obj;
152
+ nghttp3_ssize rv;
153
+
154
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
155
+
156
+ if (obj->encoder == NULL) {
157
+ rb_raise(rb_eNghttp3InvalidStateError, "Encoder is not initialized");
158
+ }
159
+
160
+ Check_Type(rb_data, T_STRING);
161
+
162
+ rv = nghttp3_qpack_encoder_read_decoder(
163
+ obj->encoder, (const uint8_t *)RSTRING_PTR(rb_data), RSTRING_LEN(rb_data));
164
+
165
+ if (rv < 0) {
166
+ nghttp3_rb_raise((int)rv, "Failed to read decoder stream");
167
+ }
168
+
169
+ return LL2NUM(rv);
170
+ }
171
+
172
+ /*
173
+ * call-seq:
174
+ * encoder.max_dtable_capacity = capacity
175
+ *
176
+ * Sets the maximum dynamic table capacity.
177
+ */
178
+ static VALUE rb_nghttp3_qpack_encoder_set_max_dtable_capacity(VALUE self,
179
+ VALUE rb_cap) {
180
+ EncoderObj *obj;
181
+ size_t cap;
182
+
183
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
184
+
185
+ if (obj->encoder == NULL) {
186
+ rb_raise(rb_eNghttp3InvalidStateError, "Encoder is not initialized");
187
+ }
188
+
189
+ cap = NUM2SIZET(rb_cap);
190
+ nghttp3_qpack_encoder_set_max_dtable_capacity(obj->encoder, cap);
191
+
192
+ return rb_cap;
193
+ }
194
+
195
+ /*
196
+ * call-seq:
197
+ * encoder.max_blocked_streams = num
198
+ *
199
+ * Sets the maximum number of blocked streams.
200
+ */
201
+ static VALUE rb_nghttp3_qpack_encoder_set_max_blocked_streams(VALUE self,
202
+ VALUE rb_num) {
203
+ EncoderObj *obj;
204
+ size_t num;
205
+
206
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
207
+
208
+ if (obj->encoder == NULL) {
209
+ rb_raise(rb_eNghttp3InvalidStateError, "Encoder is not initialized");
210
+ }
211
+
212
+ num = NUM2SIZET(rb_num);
213
+ nghttp3_qpack_encoder_set_max_blocked_streams(obj->encoder, num);
214
+
215
+ return rb_num;
216
+ }
217
+
218
+ /*
219
+ * call-seq:
220
+ * encoder.num_blocked_streams -> Integer
221
+ *
222
+ * Returns the number of currently blocked streams.
223
+ */
224
+ static VALUE rb_nghttp3_qpack_encoder_get_num_blocked_streams(VALUE self) {
225
+ EncoderObj *obj;
226
+ size_t num;
227
+
228
+ TypedData_Get_Struct(self, EncoderObj, &encoder_data_type, obj);
229
+
230
+ if (obj->encoder == NULL) {
231
+ rb_raise(rb_eNghttp3InvalidStateError, "Encoder is not initialized");
232
+ }
233
+
234
+ num = nghttp3_qpack_encoder_get_num_blocked_streams(obj->encoder);
235
+
236
+ return SIZET2NUM(num);
237
+ }
238
+
239
+ /* ============== Decoder ============== */
240
+
241
+ typedef struct {
242
+ nghttp3_qpack_decoder *decoder;
243
+ VALUE stream_contexts; /* Hash: stream_id => nghttp3_qpack_stream_context* */
244
+ size_t hard_max_dtable_capacity;
245
+ size_t max_blocked_streams;
246
+ } DecoderObj;
247
+
248
+ static void decoder_mark(void *ptr) {
249
+ DecoderObj *obj = (DecoderObj *)ptr;
250
+ if (obj->stream_contexts != Qnil) {
251
+ rb_gc_mark(obj->stream_contexts);
252
+ }
253
+ }
254
+
255
+ /* Callback to free stream contexts */
256
+ static int free_stream_context_i(VALUE key, VALUE val, VALUE arg) {
257
+ nghttp3_qpack_stream_context *sctx = (nghttp3_qpack_stream_context *)val;
258
+ if (sctx != NULL) {
259
+ nghttp3_qpack_stream_context_del(sctx);
260
+ }
261
+ return ST_CONTINUE;
262
+ }
263
+
264
+ static void decoder_free(void *ptr) {
265
+ DecoderObj *obj = (DecoderObj *)ptr;
266
+
267
+ /* Free all stream contexts */
268
+ if (!NIL_P(obj->stream_contexts)) {
269
+ rb_hash_foreach(obj->stream_contexts, free_stream_context_i, Qnil);
270
+ }
271
+
272
+ if (obj->decoder != NULL) {
273
+ nghttp3_qpack_decoder_del(obj->decoder);
274
+ obj->decoder = NULL;
275
+ }
276
+ xfree(ptr);
277
+ }
278
+
279
+ static size_t decoder_memsize(const void *ptr) { return sizeof(DecoderObj); }
280
+
281
+ static const rb_data_type_t decoder_data_type = {
282
+ .wrap_struct_name = "nghttp3_qpack_decoder_rb",
283
+ .function =
284
+ {
285
+ .dmark = decoder_mark,
286
+ .dfree = decoder_free,
287
+ .dsize = decoder_memsize,
288
+ },
289
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
290
+ };
291
+
292
+ static VALUE decoder_alloc(VALUE klass) {
293
+ DecoderObj *obj;
294
+ VALUE self =
295
+ TypedData_Make_Struct(klass, DecoderObj, &decoder_data_type, obj);
296
+ obj->decoder = NULL;
297
+ obj->stream_contexts = Qnil;
298
+ obj->hard_max_dtable_capacity = 0;
299
+ obj->max_blocked_streams = 0;
300
+ return self;
301
+ }
302
+
303
+ /*
304
+ * call-seq:
305
+ * Decoder.new(max_dtable_capacity, max_blocked_streams) -> Decoder
306
+ *
307
+ * Creates a new QPACK decoder.
308
+ */
309
+ static VALUE rb_nghttp3_qpack_decoder_initialize(VALUE self,
310
+ VALUE rb_max_capacity,
311
+ VALUE rb_max_blocked) {
312
+ DecoderObj *obj;
313
+ size_t max_capacity, max_blocked;
314
+ int rv;
315
+
316
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
317
+
318
+ max_capacity = NUM2SIZET(rb_max_capacity);
319
+ max_blocked = NUM2SIZET(rb_max_blocked);
320
+ obj->hard_max_dtable_capacity = max_capacity;
321
+ obj->max_blocked_streams = max_blocked;
322
+ obj->stream_contexts = rb_hash_new();
323
+
324
+ rv = nghttp3_qpack_decoder_new(&obj->decoder, max_capacity, max_blocked,
325
+ nghttp3_mem_default());
326
+
327
+ if (rv != 0) {
328
+ nghttp3_rb_raise(rv, "Failed to create QPACK decoder");
329
+ }
330
+
331
+ return self;
332
+ }
333
+
334
+ /*
335
+ * Get or create a stream context for the given stream_id
336
+ */
337
+ static nghttp3_qpack_stream_context *
338
+ get_or_create_stream_context(DecoderObj *obj, int64_t stream_id) {
339
+ VALUE rb_stream_id = LL2NUM(stream_id);
340
+ VALUE existing;
341
+
342
+ if (NIL_P(obj->stream_contexts)) {
343
+ return NULL;
344
+ }
345
+
346
+ existing = rb_hash_aref(obj->stream_contexts, rb_stream_id);
347
+
348
+ if (existing != Qnil && existing != Qfalse && existing != INT2FIX(0)) {
349
+ /* Return existing context */
350
+ return (nghttp3_qpack_stream_context *)NUM2ULL(existing);
351
+ }
352
+
353
+ /* Create new context */
354
+ nghttp3_qpack_stream_context *sctx;
355
+ int rv = nghttp3_qpack_stream_context_new(&sctx, stream_id,
356
+ nghttp3_mem_default());
357
+
358
+ if (rv != 0) {
359
+ return NULL;
360
+ }
361
+
362
+ /* Store as Integer (pointer value) */
363
+ rb_hash_aset(obj->stream_contexts, rb_stream_id, ULL2NUM((uintptr_t)sctx));
364
+
365
+ return sctx;
366
+ }
367
+
368
+ /*
369
+ * Helper to convert nghttp3_rcbuf to Ruby String and decref
370
+ */
371
+ static VALUE rcbuf_to_string(nghttp3_rcbuf *rcbuf) {
372
+ nghttp3_vec vec = nghttp3_rcbuf_get_buf(rcbuf);
373
+ VALUE str = rb_str_new((const char *)vec.base, vec.len);
374
+ nghttp3_rcbuf_decref(rcbuf);
375
+ return str;
376
+ }
377
+
378
+ /*
379
+ * call-seq:
380
+ * decoder.decode(stream_id, data, fin: false) -> Hash
381
+ *
382
+ * Decodes headers. Returns a Hash with :headers, :blocked, :consumed.
383
+ */
384
+ static VALUE rb_nghttp3_qpack_decoder_decode(int argc, VALUE *argv,
385
+ VALUE self) {
386
+ VALUE rb_stream_id, rb_data, rb_opts;
387
+ VALUE rb_fin = Qfalse;
388
+ DecoderObj *obj;
389
+ int64_t stream_id;
390
+ nghttp3_qpack_stream_context *sctx;
391
+ nghttp3_qpack_nv nv;
392
+ uint8_t flags;
393
+ nghttp3_ssize rv;
394
+ const uint8_t *src;
395
+ size_t srclen;
396
+ int fin;
397
+ VALUE result, headers;
398
+ size_t total_consumed = 0;
399
+ int blocked = 0;
400
+
401
+ rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_data, &rb_opts);
402
+
403
+ if (!NIL_P(rb_opts)) {
404
+ rb_fin = rb_hash_aref(rb_opts, ID2SYM(rb_intern("fin")));
405
+ }
406
+
407
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
408
+
409
+ if (obj->decoder == NULL) {
410
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
411
+ }
412
+
413
+ stream_id = NUM2LL(rb_stream_id);
414
+ Check_Type(rb_data, T_STRING);
415
+ src = (const uint8_t *)RSTRING_PTR(rb_data);
416
+ srclen = RSTRING_LEN(rb_data);
417
+ fin = RTEST(rb_fin) ? 1 : 0;
418
+
419
+ sctx = get_or_create_stream_context(obj, stream_id);
420
+ if (sctx == NULL) {
421
+ rb_raise(rb_eNghttp3NoMemError, "Failed to create stream context");
422
+ }
423
+
424
+ headers = rb_ary_new();
425
+
426
+ while (srclen > 0 || fin) {
427
+ rv = nghttp3_qpack_decoder_read_request(obj->decoder, sctx, &nv, &flags,
428
+ src, srclen, fin);
429
+
430
+ if (rv < 0) {
431
+ nghttp3_rb_raise((int)rv, "Failed to decode headers");
432
+ }
433
+
434
+ total_consumed += (size_t)rv;
435
+ src += rv;
436
+ srclen -= (size_t)rv;
437
+
438
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
439
+ blocked = 1;
440
+ break;
441
+ }
442
+
443
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
444
+ VALUE header = rb_hash_new();
445
+ rb_hash_aset(header, ID2SYM(rb_intern("name")),
446
+ rcbuf_to_string(nv.name));
447
+ rb_hash_aset(header, ID2SYM(rb_intern("value")),
448
+ rcbuf_to_string(nv.value));
449
+ rb_hash_aset(header, ID2SYM(rb_intern("token")), INT2NUM(nv.token));
450
+ rb_ary_push(headers, header);
451
+ }
452
+
453
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
454
+ /* Remove stream context */
455
+ if (!NIL_P(obj->stream_contexts)) {
456
+ VALUE rb_sid = LL2NUM(stream_id);
457
+ VALUE ctx_val = rb_hash_aref(obj->stream_contexts, rb_sid);
458
+ if (!NIL_P(ctx_val)) {
459
+ nghttp3_qpack_stream_context *old_sctx =
460
+ (nghttp3_qpack_stream_context *)NUM2ULL(ctx_val);
461
+ nghttp3_qpack_stream_context_del(old_sctx);
462
+ rb_hash_delete(obj->stream_contexts, rb_sid);
463
+ }
464
+ }
465
+ break;
466
+ }
467
+
468
+ if (rv == 0 && srclen == 0) {
469
+ break;
470
+ }
471
+ }
472
+
473
+ result = rb_hash_new();
474
+ rb_hash_aset(result, ID2SYM(rb_intern("headers")),
475
+ blocked ? Qnil : headers);
476
+ rb_hash_aset(result, ID2SYM(rb_intern("blocked")),
477
+ blocked ? Qtrue : Qfalse);
478
+ rb_hash_aset(result, ID2SYM(rb_intern("consumed")), SIZET2NUM(total_consumed));
479
+
480
+ return result;
481
+ }
482
+
483
+ /*
484
+ * call-seq:
485
+ * decoder.read_encoder(data) -> Integer
486
+ *
487
+ * Reads encoder stream data. Returns number of bytes consumed.
488
+ */
489
+ static VALUE rb_nghttp3_qpack_decoder_read_encoder(VALUE self, VALUE rb_data) {
490
+ DecoderObj *obj;
491
+ nghttp3_ssize rv;
492
+
493
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
494
+
495
+ if (obj->decoder == NULL) {
496
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
497
+ }
498
+
499
+ Check_Type(rb_data, T_STRING);
500
+
501
+ rv = nghttp3_qpack_decoder_read_encoder(
502
+ obj->decoder, (const uint8_t *)RSTRING_PTR(rb_data), RSTRING_LEN(rb_data));
503
+
504
+ if (rv < 0) {
505
+ nghttp3_rb_raise((int)rv, "Failed to read encoder stream");
506
+ }
507
+
508
+ return LL2NUM(rv);
509
+ }
510
+
511
+ /*
512
+ * call-seq:
513
+ * decoder.decoder_stream_data -> String
514
+ *
515
+ * Returns data to be sent on the decoder stream.
516
+ */
517
+ static VALUE rb_nghttp3_qpack_decoder_decoder_stream_data(VALUE self) {
518
+ DecoderObj *obj;
519
+ nghttp3_buf dbuf;
520
+ size_t len;
521
+ VALUE result;
522
+
523
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
524
+
525
+ if (obj->decoder == NULL) {
526
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
527
+ }
528
+
529
+ len = nghttp3_qpack_decoder_get_decoder_streamlen(obj->decoder);
530
+
531
+ if (len == 0) {
532
+ return rb_str_new("", 0);
533
+ }
534
+
535
+ /* Allocate buffer */
536
+ nghttp3_buf_init(&dbuf);
537
+ dbuf.begin = ALLOC_N(uint8_t, len);
538
+ dbuf.pos = dbuf.begin;
539
+ dbuf.last = dbuf.begin;
540
+ dbuf.end = dbuf.begin + len;
541
+
542
+ nghttp3_qpack_decoder_write_decoder(obj->decoder, &dbuf);
543
+
544
+ result = rb_str_new((const char *)dbuf.pos, nghttp3_buf_len(&dbuf));
545
+
546
+ xfree(dbuf.begin);
547
+
548
+ return result;
549
+ }
550
+
551
+ /*
552
+ * call-seq:
553
+ * decoder.cancel_stream(stream_id) -> nil
554
+ *
555
+ * Cancels decoding for the given stream.
556
+ */
557
+ static VALUE rb_nghttp3_qpack_decoder_cancel_stream(VALUE self,
558
+ VALUE rb_stream_id) {
559
+ DecoderObj *obj;
560
+ int64_t stream_id;
561
+ int rv;
562
+
563
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
564
+
565
+ if (obj->decoder == NULL) {
566
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
567
+ }
568
+
569
+ stream_id = NUM2LL(rb_stream_id);
570
+
571
+ /* Remove stream context */
572
+ if (!NIL_P(obj->stream_contexts)) {
573
+ VALUE rb_sid = LL2NUM(stream_id);
574
+ VALUE ctx_val = rb_hash_aref(obj->stream_contexts, rb_sid);
575
+ if (!NIL_P(ctx_val)) {
576
+ nghttp3_qpack_stream_context *sctx =
577
+ (nghttp3_qpack_stream_context *)NUM2ULL(ctx_val);
578
+ nghttp3_qpack_stream_context_del(sctx);
579
+ rb_hash_delete(obj->stream_contexts, rb_sid);
580
+ }
581
+ }
582
+
583
+ rv = nghttp3_qpack_decoder_cancel_stream(obj->decoder, stream_id);
584
+
585
+ if (rv != 0) {
586
+ nghttp3_rb_raise(rv, "Failed to cancel stream");
587
+ }
588
+
589
+ return Qnil;
590
+ }
591
+
592
+ /*
593
+ * call-seq:
594
+ * decoder.max_dtable_capacity = capacity
595
+ *
596
+ * Sets the maximum dynamic table capacity.
597
+ */
598
+ static VALUE rb_nghttp3_qpack_decoder_set_max_dtable_capacity(VALUE self,
599
+ VALUE rb_cap) {
600
+ DecoderObj *obj;
601
+ size_t cap;
602
+ int rv;
603
+
604
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
605
+
606
+ if (obj->decoder == NULL) {
607
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
608
+ }
609
+
610
+ cap = NUM2SIZET(rb_cap);
611
+ rv = nghttp3_qpack_decoder_set_max_dtable_capacity(obj->decoder, cap);
612
+
613
+ if (rv != 0) {
614
+ nghttp3_rb_raise(rv, "Failed to set max dtable capacity");
615
+ }
616
+
617
+ return rb_cap;
618
+ }
619
+
620
+ /*
621
+ * call-seq:
622
+ * decoder.insert_count -> Integer
623
+ *
624
+ * Returns the current insert count.
625
+ */
626
+ static VALUE rb_nghttp3_qpack_decoder_get_insert_count(VALUE self) {
627
+ DecoderObj *obj;
628
+ uint64_t icnt;
629
+
630
+ TypedData_Get_Struct(self, DecoderObj, &decoder_data_type, obj);
631
+
632
+ if (obj->decoder == NULL) {
633
+ rb_raise(rb_eNghttp3InvalidStateError, "Decoder is not initialized");
634
+ }
635
+
636
+ icnt = nghttp3_qpack_decoder_get_icnt(obj->decoder);
637
+
638
+ return ULL2NUM(icnt);
639
+ }
640
+
641
+ void Init_nghttp3_qpack(void) {
642
+ /* Define QPACK module */
643
+ rb_mNghttp3QPACK = rb_define_module_under(rb_mNghttp3, "QPACK");
644
+
645
+ /* Define Encoder class */
646
+ rb_cNghttp3QPACKEncoder =
647
+ rb_define_class_under(rb_mNghttp3QPACK, "Encoder", rb_cObject);
648
+ rb_define_alloc_func(rb_cNghttp3QPACKEncoder, encoder_alloc);
649
+ rb_define_method(rb_cNghttp3QPACKEncoder, "initialize",
650
+ rb_nghttp3_qpack_encoder_initialize, 1);
651
+ rb_define_method(rb_cNghttp3QPACKEncoder, "encode",
652
+ rb_nghttp3_qpack_encoder_encode, 2);
653
+ rb_define_method(rb_cNghttp3QPACKEncoder, "read_decoder",
654
+ rb_nghttp3_qpack_encoder_read_decoder, 1);
655
+ rb_define_method(rb_cNghttp3QPACKEncoder, "max_dtable_capacity=",
656
+ rb_nghttp3_qpack_encoder_set_max_dtable_capacity, 1);
657
+ rb_define_method(rb_cNghttp3QPACKEncoder, "max_blocked_streams=",
658
+ rb_nghttp3_qpack_encoder_set_max_blocked_streams, 1);
659
+ rb_define_method(rb_cNghttp3QPACKEncoder, "num_blocked_streams",
660
+ rb_nghttp3_qpack_encoder_get_num_blocked_streams, 0);
661
+
662
+ /* Define Decoder class */
663
+ rb_cNghttp3QPACKDecoder =
664
+ rb_define_class_under(rb_mNghttp3QPACK, "Decoder", rb_cObject);
665
+ rb_define_alloc_func(rb_cNghttp3QPACKDecoder, decoder_alloc);
666
+ rb_define_method(rb_cNghttp3QPACKDecoder, "initialize",
667
+ rb_nghttp3_qpack_decoder_initialize, 2);
668
+ rb_define_method(rb_cNghttp3QPACKDecoder, "decode",
669
+ rb_nghttp3_qpack_decoder_decode, -1);
670
+ rb_define_method(rb_cNghttp3QPACKDecoder, "read_encoder",
671
+ rb_nghttp3_qpack_decoder_read_encoder, 1);
672
+ rb_define_method(rb_cNghttp3QPACKDecoder, "decoder_stream_data",
673
+ rb_nghttp3_qpack_decoder_decoder_stream_data, 0);
674
+ rb_define_method(rb_cNghttp3QPACKDecoder, "cancel_stream",
675
+ rb_nghttp3_qpack_decoder_cancel_stream, 1);
676
+ rb_define_method(rb_cNghttp3QPACKDecoder, "max_dtable_capacity=",
677
+ rb_nghttp3_qpack_decoder_set_max_dtable_capacity, 1);
678
+ rb_define_method(rb_cNghttp3QPACKDecoder, "insert_count",
679
+ rb_nghttp3_qpack_decoder_get_insert_count, 0);
680
+ }