protocol-hpack 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
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