protocol-hpack 1.5.0 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d664c70ae90134d28bf5491fe5f46612ce17292d8ebdc06daba9e423459501e
4
- data.tar.gz: 0ede73ffcfb63a755022f3212309ce6ce4a74dcebd819f0287f8cf610a17fbee
3
+ metadata.gz: ad81d71708c7c92bb94e9616655872045a31052aa62c3543487064ddea8c99ad
4
+ data.tar.gz: 1945361f8e8576ef3db44de1e0cf44554795699d910c40b8659232a499ded6b8
5
5
  SHA512:
6
- metadata.gz: a194f5bbdfdbb5552f0ca6216b9cbb314bb5590a10316d51f99578961ebd3247bfa3aea6f3f8430f0e2f34ec89e9a18574c7080988f5cfde571b58ed8d6b143e
7
- data.tar.gz: e11f0747b74859d27a2834350e46c49ea573e71f772687b387d6b4e4d1fd6b3033bc2c99537961fe2c59944e0acb2e0aeb78c0d7a9dbeb681cb04391d7e0ebb2
6
+ metadata.gz: c0537372d9278691fa9621622b84f32facde4f9efdfbfd0c36eb7573bba4b19ec05bc91f5191a7463405cbed952c4c6ac614531c4c0fc0b26707196db8d800b3
7
+ data.tar.gz: fc53196d54e9b02a56df5c1294e819f5a4002c49201811c12d2666725a0325e27df75603afc4b04a5fc5c1845237937097bf51352ea4d5f1c3741d1b73fa244a
checksums.yaml.gz.sig CHANGED
Binary file
@@ -13,8 +13,8 @@
13
13
  # Copyright, 2020, by Justin Mazzocchi.
14
14
  # Copyright, 2024, by Nathan Froyd.
15
15
 
16
- require_relative 'context'
17
- require_relative 'huffman'
16
+ require_relative "context"
17
+ require_relative "huffman"
18
18
 
19
19
  module Protocol
20
20
  module HPACK
@@ -75,22 +75,19 @@ module Protocol
75
75
  # @param bits [Integer] number of available bits
76
76
  # @return [String] binary string
77
77
  def write_integer(value, bits)
78
- limit = 2**bits - 1
78
+ limit = (1 << bits) - 1
79
79
 
80
- return write_bytes([value].pack('C')) if value < limit
80
+ return @buffer << value if value < limit
81
81
 
82
- bytes = []
83
- bytes.push(limit) unless bits.zero?
82
+ @buffer << limit unless bits.zero?
84
83
 
85
84
  value -= limit
86
85
  while value >= 128
87
- bytes.push((value % 128) + 128)
86
+ @buffer << ((value & 0x7f) + 128)
88
87
  value /= 128
89
88
  end
90
89
 
91
- bytes.push(value)
92
-
93
- write_bytes(bytes.pack('C*'))
90
+ @buffer << value
94
91
  end
95
92
 
96
93
  def huffman
@@ -5,7 +5,7 @@
5
5
  # Copyright, 2024, by Maruth Goyal.
6
6
  # Copyright, 2024, by Nathan Froyd.
7
7
 
8
- require_relative 'huffman'
8
+ require_relative "huffman"
9
9
 
10
10
  module Protocol
11
11
  # Implementation of header compression for HTTP 2.0 (HPACK) format adapted
@@ -5,8 +5,8 @@
5
5
  # Copyright, 2024, by Maruth Goyal.
6
6
  # Copyright, 2024, by Nathan Froyd.
7
7
 
8
- require_relative 'context'
9
- require_relative 'huffman'
8
+ require_relative "context"
9
+ require_relative "huffman"
10
10
 
11
11
  module Protocol
12
12
  module HPACK
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2014, by Kaoru Maeda.
5
+ # Copyright, 2015, by Tamir Duberstein.
6
+ # Copyright, 2015, by Ilya Grigorik.
7
+ # Copyright, 2016, by George Ulmer.
8
+ # Copyright, 2018-2024, by Samuel Williams.
9
+ # Copyright, 2024, by Nathan Froyd.
10
+
11
+ require_relative "../huffman"
12
+ require "set"
13
+
14
+ module Protocol
15
+ module HPACK
16
+ class Huffman
17
+ module Generator
18
+ EOS = 256
19
+
20
+ class Node
21
+ attr_accessor :next, :emit, :final, :depth
22
+ attr_accessor :transitions
23
+ attr_accessor :id
24
+
25
+ @@id = 0
26
+
27
+ def initialize(depth)
28
+ @next = [nil, nil]
29
+ @id = @@id
30
+ @@id += 1
31
+ @final = false
32
+ @depth = depth
33
+ end
34
+
35
+ def add(code, len, chr)
36
+ self.final = true if chr == EOS && @depth <= 7
37
+ if len.zero?
38
+ @emit = chr
39
+ else
40
+ bit = (code & (1 << (len - 1))).zero? ? 0 : 1
41
+ node = @next[bit] ||= Node.new(@depth + 1)
42
+ node.add(code, len - 1, chr)
43
+ end
44
+ end
45
+
46
+ class Transition
47
+ attr_accessor :emit, :node
48
+ def initialize(emit, node)
49
+ @emit = emit
50
+ @node = node
51
+ end
52
+ end
53
+
54
+ def self.generate_tree
55
+ @root = new(0)
56
+ Protocol::HPACK::Huffman::CODES.each_with_index do |c, chr|
57
+ code, len = c
58
+ @root.add(code, len, chr)
59
+ end
60
+ puts "#{@@id} nodes"
61
+ @root
62
+ end
63
+
64
+ def self.generate_machine
65
+ generate_tree
66
+
67
+ # Using un-ordered sets (potentially) produces non-deterministic results:
68
+ togo = Set[@root]
69
+ @states = Set[@root]
70
+
71
+ until togo.empty?
72
+ node = togo.first
73
+ togo.delete(node)
74
+
75
+ next if node.transitions
76
+ node.transitions = Array[1 << BITS_AT_ONCE]
77
+
78
+ (1 << BITS_AT_ONCE).times do |input|
79
+ n = node
80
+ emit = +""
81
+ (BITS_AT_ONCE - 1).downto(0) do |i|
82
+ bit = (input & (1 << i)).zero? ? 0 : 1
83
+ n = n.next[bit]
84
+ next unless n.emit
85
+ if n.emit == EOS
86
+ emit = EOS # cause error on decoding
87
+ else
88
+ emit << n.emit.chr(Encoding::BINARY) unless emit == EOS
89
+ end
90
+ n = @root
91
+ end
92
+ node.transitions[input] = Transition.new(emit, n)
93
+ togo << n
94
+ @states << n
95
+ end
96
+ end
97
+ puts "#{@states.size} states"
98
+ @root
99
+ end
100
+
101
+ MACHINE_PATH = File.expand_path("machine.rb", __dir__)
102
+
103
+ def self.generate_state_table(output_path = MACHINE_PATH)
104
+ generate_machine
105
+ state_id = {}
106
+ id_state = {}
107
+ state_id[@root] = 0
108
+ id_state[0] = @root
109
+ max_final = 0
110
+ id = 1
111
+ (@states - [@root]).sort_by {|s| s.final ? 0 : 1}.each do |s|
112
+ state_id[s] = id
113
+ id_state[id] = s
114
+ max_final = id if s.final
115
+ id += 1
116
+ end
117
+
118
+ File.open(output_path, "w") do |file|
119
+ file.print <<~HEADER
120
+ # frozen_string_literal: true
121
+
122
+ # Released under the MIT License.
123
+ # Copyright, 2018-2024, by Samuel Williams.
124
+
125
+ # Machine generated Huffman decoder state machine.
126
+ # DO NOT EDIT THIS FILE.
127
+
128
+ module Protocol
129
+ module HPACK
130
+ class Huffman
131
+ # :nodoc:
132
+ MAX_FINAL_STATE = #{max_final}
133
+ MACHINE = [
134
+ HEADER
135
+
136
+ id.times do |i|
137
+ n = id_state[i]
138
+ file.print "\t\t\t\t["
139
+ string = (1 << BITS_AT_ONCE).times.map do |t|
140
+ transition = n.transitions.fetch(t)
141
+ emit = transition.emit
142
+ unless emit == EOS
143
+ bytes = emit.bytes
144
+ fail ArgumentError if bytes.size > 1
145
+ emit = bytes.first
146
+ end
147
+ "[#{emit.inspect}, #{state_id.fetch(transition.node)}]"
148
+ end.join(", ")
149
+ file.print(string)
150
+ file.print "],\n"
151
+ end
152
+
153
+ file.print <<~FOOTER
154
+ ].each {|arr| arr.each {|subarr| subarr.each(&:freeze)}.freeze}.freeze
155
+ end
156
+ end
157
+ end
158
+ FOOTER
159
+ end
160
+ end
161
+
162
+ class << self
163
+ attr_reader :root
164
+ end
165
+
166
+ # Test decoder
167
+ def self.decode(input)
168
+ emit = ""
169
+ n = root
170
+ nibbles = input.unpack("C*").flat_map {|b| [((b & 0xf0) >> 4), b & 0xf]}
171
+ until nibbles.empty?
172
+ nb = nibbles.shift
173
+ t = n.transitions[nb]
174
+ emit << t.emit
175
+ n = t.node
176
+ end
177
+ unless n.final && nibbles.all? {|x| x == 0xf}
178
+ puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}"
179
+ end
180
+ emit
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end