/
packed_string.rb
67 lines (51 loc) · 1.48 KB
/
packed_string.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# frozen_string_literal: true
class PackedString
class FastIdList
def self.from_array(array, pack_with)
new(array.sort.pack("#{pack_with}*"), pack_with)
end
def self.from_string(string, pack_with)
new(string, pack_with)
end
def initialize(packed, pack_with)
raise "unknown pack format, expecting Q or V" if pack_with != "V" && pack_with != "Q"
@packed = packed
@pack_with = pack_with
@slot_size = pack_with == "V" ? 4 : 8
end
def include?(id)
found = (0...length).bsearch do |index|
@packed.byteslice(index * @slot_size, @slot_size).unpack1(@pack_with) >= id
end
found && @packed.byteslice(found * @slot_size, @slot_size).unpack1(@pack_with) == id
end
def length
@length ||= @packed.bytesize / @slot_size
end
def to_a
@packed.unpack("#{@pack_with}*")
end
def to_s
@packed
end
end
def initialize(pack_with = "V")
@pack_with = pack_with
@oj_options = { mode: :compat }
end
def encode(hash)
if user_ids = hash["user_ids"]
hash["user_ids"] = FastIdList.from_array(hash["user_ids"], @pack_with).to_s
end
hash["data"] = ::Oj.dump(hash["data"], @oj_options)
Marshal.dump(hash)
end
def decode(payload)
result = Marshal.load(payload)
result["data"] = ::Oj.load(result["data"], @oj_options)
if str = result["user_ids"]
result["user_ids"] = FastIdList.from_string(str, @pack_with)
end
result
end
end