class YARP::Serialize::Loader

def initialize(source, serialized)

def initialize(source, serialized)
  @encoding = Encoding::UTF_8
  @input = source.source.dup
  @serialized = serialized
  @io = StringIO.new(serialized)
  @io.set_encoding(Encoding::BINARY)
  @constant_pool_offset = nil
  @constant_pool = nil
  @source = source
end

def load

def load
  raise "Invalid serialization" if io.read(4) != "YARP"
  raise "Invalid serialization" if io.read(3).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION]
  @encoding = Encoding.find(io.read(load_varint))
  @input = input.force_encoding(@encoding).freeze
  comments = load_varint.times.map { Comment.new(Comment::TYPES.fetch(io.getbyte), load_location) }
  errors = load_varint.times.map { ParseError.new(load_string, load_location) }
  warnings = load_varint.times.map { ParseWarning.new(load_string, load_location) }
  @constant_pool_offset = io.read(4).unpack1("L")
  @constant_pool = Array.new(load_varint, nil)
  ast = load_node
  YARP::ParseResult.new(ast, comments, errors, warnings, @source)
end

def load_constant

def load_constant
  index = load_varint - 1
  constant = constant_pool[index]
  unless constant
    offset = constant_pool_offset + index * 8
    start = serialized.unpack1("L", offset: offset)
    length = serialized.unpack1("L", offset: offset + 4)
    constant = input.byteslice(start, length).to_sym
    constant_pool[index] = constant
  end
  constant
end

def load_location

def load_location
  Location.new(source, load_varint, load_varint)
end

def load_node

def load_node
  type = io.getbyte
  location = load_location
  case type
  when 1 then
    AliasNode.new(load_node, load_node, load_location, location)
  when 2 then
    AlternationPatternNode.new(load_node, load_node, load_location, location)
  when 3 then
    AndNode.new(load_node, load_node, load_location, location)
  when 4 then
    AndWriteNode.new(load_node, load_node, load_location, location)
  when 5 then
    ArgumentsNode.new(Array.new(load_varint) { load_node }, location)
  when 6 then
    ArrayNode.new(Array.new(load_varint) { load_node }, load_optional_location, load_optional_location, location)
  when 7 then
    ArrayPatternNode.new(load_optional_node, Array.new(load_varint) { load_node }, load_optional_node, Array.new(load_varint) { load_node }, load_optional_location, load_optional_location, location)
  when 8 then
    AssocNode.new(load_node, load_optional_node, load_optional_location, location)
  when 9 then
    AssocSplatNode.new(load_optional_node, load_location, location)
  when 10 then
    BackReferenceReadNode.new(location)
  when 11 then
    BeginNode.new(load_optional_location, load_optional_node, load_optional_node, load_optional_node, load_optional_node, load_optional_location, location)
  when 12 then
    BlockArgumentNode.new(load_optional_node, load_location, location)
  when 13 then
    BlockNode.new(Array.new(load_varint) { load_constant }, load_optional_node, load_optional_node, load_location, load_location, location)
  when 14 then
    BlockParameterNode.new(load_optional_location, load_location, location)
  when 15 then
    BlockParametersNode.new(load_optional_node, Array.new(load_varint) { load_location }, load_optional_location, load_optional_location, location)
  when 16 then
    BreakNode.new(load_optional_node, load_location, location)
  when 17 then
    CallNode.new(load_optional_node, load_optional_location, load_optional_location, load_optional_location, load_optional_node, load_optional_location, load_optional_node, load_varint, load_string, location)
  when 18 then
    CallOperatorAndWriteNode.new(load_node, load_location, load_node, location)
  when 19 then
    CallOperatorOrWriteNode.new(load_node, load_node, load_location, location)
  when 20 then
    CallOperatorWriteNode.new(load_node, load_location, load_node, load_constant, location)
  when 21 then
    CapturePatternNode.new(load_node, load_node, load_location, location)
  when 22 then
    CaseNode.new(load_optional_node, Array.new(load_varint) { load_node }, load_optional_node, load_location, load_location, location)
  when 23 then
    ClassNode.new(Array.new(load_varint) { load_constant }, load_location, load_node, load_optional_location, load_optional_node, load_optional_node, load_location, location)
  when 24 then
    ClassVariableReadNode.new(location)
  when 25 then
    ClassVariableWriteNode.new(load_location, load_optional_node, load_optional_location, location)
  when 26 then
    ConstantPathNode.new(load_optional_node, load_node, load_location, location)
  when 27 then
    ConstantPathWriteNode.new(load_node, load_optional_location, load_optional_node, location)
  when 28 then
    ConstantReadNode.new(location)
  when 29 then
    ConstantWriteNode.new(load_location, load_optional_node, load_optional_location, location)
  when 30 then
    load_serialized_length
    DefNode.new(load_location, load_optional_node, load_optional_node, load_optional_node, Array.new(load_varint) { load_constant }, load_location, load_optional_location, load_optional_location, load_optional_location, load_optional_location, load_optional_location, location)
  when 31 then
    DefinedNode.new(load_optional_location, load_node, load_optional_location, load_location, location)
  when 32 then
    ElseNode.new(load_location, load_optional_node, load_optional_location, location)
  when 33 then
    EmbeddedStatementsNode.new(load_location, load_optional_node, load_location, location)
  when 34 then
    EmbeddedVariableNode.new(load_location, load_node, location)
  when 35 then
    EnsureNode.new(load_location, load_optional_node, load_location, location)
  when 36 then
    FalseNode.new(location)
  when 37 then
    FindPatternNode.new(load_optional_node, load_node, Array.new(load_varint) { load_node }, load_node, load_optional_location, load_optional_location, location)
  when 38 then
    FlipFlopNode.new(load_optional_node, load_optional_node, load_location, load_varint, location)
  when 39 then
    FloatNode.new(location)
  when 40 then
    ForNode.new(load_node, load_node, load_optional_node, load_location, load_location, load_optional_location, load_location, location)
  when 41 then
    ForwardingArgumentsNode.new(location)
  when 42 then
    ForwardingParameterNode.new(location)
  when 43 then
    ForwardingSuperNode.new(load_optional_node, location)
  when 44 then
    GlobalVariableReadNode.new(location)
  when 45 then
    GlobalVariableWriteNode.new(load_location, load_optional_location, load_optional_node, location)
  when 46 then
    HashNode.new(load_location, Array.new(load_varint) { load_node }, load_location, location)
  when 47 then
    HashPatternNode.new(load_optional_node, Array.new(load_varint) { load_node }, load_optional_node, load_optional_location, load_optional_location, location)
  when 48 then
    IfNode.new(load_optional_location, load_node, load_optional_node, load_optional_node, load_optional_location, location)
  when 49 then
    ImaginaryNode.new(load_node, location)
  when 50 then
    InNode.new(load_node, load_optional_node, load_location, load_optional_location, location)
  when 51 then
    InstanceVariableReadNode.new(location)
  when 52 then
    InstanceVariableWriteNode.new(load_location, load_optional_node, load_optional_location, location)
  when 53 then
    IntegerNode.new(location)
  when 54 then
    InterpolatedRegularExpressionNode.new(load_location, Array.new(load_varint) { load_node }, load_location, load_varint, location)
  when 55 then
    InterpolatedStringNode.new(load_optional_location, Array.new(load_varint) { load_node }, load_optional_location, location)
  when 56 then
    InterpolatedSymbolNode.new(load_optional_location, Array.new(load_varint) { load_node }, load_optional_location, location)
  when 57 then
    InterpolatedXStringNode.new(load_location, Array.new(load_varint) { load_node }, load_location, location)
  when 58 then
    KeywordHashNode.new(Array.new(load_varint) { load_node }, location)
  when 59 then
    KeywordParameterNode.new(load_location, load_optional_node, location)
  when 60 then
    KeywordRestParameterNode.new(load_location, load_optional_location, location)
  when 61 then
    LambdaNode.new(Array.new(load_varint) { load_constant }, load_location, load_optional_node, load_optional_node, location)
  when 62 then
    LocalVariableReadNode.new(load_constant, load_varint, location)
  when 63 then
    LocalVariableWriteNode.new(load_constant, load_varint, load_optional_node, load_location, load_optional_location, location)
  when 64 then
    MatchPredicateNode.new(load_node, load_node, load_location, location)
  when 65 then
    MatchRequiredNode.new(load_node, load_node, load_location, location)
  when 66 then
    MissingNode.new(location)
  when 67 then
    ModuleNode.new(Array.new(load_varint) { load_constant }, load_location, load_node, load_optional_node, load_location, location)
  when 68 then
    MultiWriteNode.new(Array.new(load_varint) { load_node }, load_optional_location, load_optional_node, load_optional_location, load_optional_location, location)
  when 69 then
    NextNode.new(load_optional_node, load_location, location)
  when 70 then
    NilNode.new(location)
  when 71 then
    NoKeywordsParameterNode.new(load_location, load_location, location)
  when 72 then
    NumberedReferenceReadNode.new(location)
  when 73 then
    OperatorWriteNode.new(load_node, load_location, load_constant, load_node, location)
  when 74 then
    OptionalParameterNode.new(load_constant, load_location, load_location, load_node, location)
  when 75 then
    OrNode.new(load_node, load_node, load_location, location)
  when 76 then
    OrWriteNode.new(load_node, load_node, load_location, location)
  when 77 then
    ParametersNode.new(Array.new(load_varint) { load_node }, Array.new(load_varint) { load_node }, Array.new(load_varint) { load_node }, load_optional_node, Array.new(load_varint) { load_node }, load_optional_node, load_optional_node, location)
  when 78 then
    ParenthesesNode.new(load_optional_node, load_location, load_location, location)
  when 79 then
    PinnedExpressionNode.new(load_node, load_location, load_location, load_location, location)
  when 80 then
    PinnedVariableNode.new(load_node, load_location, location)
  when 81 then
    PostExecutionNode.new(load_optional_node, load_location, load_location, load_location, location)
  when 82 then
    PreExecutionNode.new(load_optional_node, load_location, load_location, load_location, location)
  when 83 then
    ProgramNode.new(Array.new(load_varint) { load_constant }, load_node, location)
  when 84 then
    RangeNode.new(load_optional_node, load_optional_node, load_location, load_varint, location)
  when 85 then
    RationalNode.new(load_node, location)
  when 86 then
    RedoNode.new(location)
  when 87 then
    RegularExpressionNode.new(load_location, load_location, load_location, load_string, load_varint, location)
  when 88 then
    RequiredDestructuredParameterNode.new(Array.new(load_varint) { load_node }, load_location, load_location, location)
  when 89 then
    RequiredParameterNode.new(load_constant, location)
  when 90 then
    RescueModifierNode.new(load_node, load_location, load_node, location)
  when 91 then
    RescueNode.new(load_location, Array.new(load_varint) { load_node }, load_optional_location, load_optional_node, load_optional_node, load_optional_node, location)
  when 92 then
    RestParameterNode.new(load_location, load_optional_location, location)
  when 93 then
    RetryNode.new(location)
  when 94 then
    ReturnNode.new(load_location, load_optional_node, location)
  when 95 then
    SelfNode.new(location)
  when 96 then
    SingletonClassNode.new(Array.new(load_varint) { load_constant }, load_location, load_location, load_node, load_optional_node, load_location, location)
  when 97 then
    SourceEncodingNode.new(location)
  when 98 then
    SourceFileNode.new(load_string, location)
  when 99 then
    SourceLineNode.new(location)
  when 100 then
    SplatNode.new(load_location, load_optional_node, location)
  when 101 then
    StatementsNode.new(Array.new(load_varint) { load_node }, location)
  when 102 then
    StringConcatNode.new(load_node, load_node, location)
  when 103 then
    StringNode.new(load_optional_location, load_location, load_optional_location, load_string, location)
  when 104 then
    SuperNode.new(load_location, load_optional_location, load_optional_node, load_optional_location, load_optional_node, location)
  when 105 then
    SymbolNode.new(load_optional_location, load_location, load_optional_location, load_string, location)
  when 106 then
    TrueNode.new(location)
  when 107 then
    UndefNode.new(Array.new(load_varint) { load_node }, load_location, location)
  when 108 then
    UnlessNode.new(load_location, load_node, load_optional_node, load_optional_node, load_optional_location, location)
  when 109 then
    UntilNode.new(load_location, load_node, load_optional_node, load_varint, location)
  when 110 then
    WhenNode.new(load_location, Array.new(load_varint) { load_node }, load_optional_node, location)
  when 111 then
    WhileNode.new(load_location, load_node, load_optional_node, load_varint, location)
  when 112 then
    XStringNode.new(load_location, load_location, load_location, load_string, location)
  when 113 then
    YieldNode.new(load_location, load_optional_location, load_optional_node, load_optional_location, location)
  end
end

def load_optional_location

def load_optional_location
  load_location if io.getbyte != 0
end

def load_optional_node

def load_optional_node
  if io.getbyte != 0
    io.pos -= 1
    load_node
  end
end

def load_serialized_length

def load_serialized_length
  io.read(4).unpack1("L")
end

def load_string

def load_string
  io.read(load_varint).force_encoding(encoding)
end

def load_tokens

def load_tokens
  tokens = []
  while type = TOKEN_TYPES.fetch(load_varint)
    start = load_varint
    length = load_varint
    lex_state = load_varint
    location = Location.new(@source, start, length)
    tokens << [YARP::Token.new(type, location.slice, location), lex_state]
  end
  comments = load_varint.times.map { Comment.new(Comment::TYPES.fetch(load_varint), load_location) }
  errors = load_varint.times.map { ParseError.new(load_string, load_location) }
  warnings = load_varint.times.map { ParseWarning.new(load_string, load_location) }
  raise "Expected to consume all bytes while deserializing" unless @io.eof?
  YARP::ParseResult.new(tokens, comments, errors, warnings, @source)
end

def load_varint

This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints
variable-length integer using https://en.wikipedia.org/wiki/LEB128
def load_varint
  n = io.getbyte
  if n < 128
    n
  else
    n -= 128
    shift = 0
    while (b = io.getbyte) >= 128
      n += (b - 128) << (shift += 7)
    end
    n + (b << (shift + 7))
  end
end