BytecodeInterpreter.swift 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. // Sources/SwiftProtobuf/BytecodeInterpreter.swift - Internal bytecode interpreter
  2. //
  3. // Copyright (c) 2014 - 2025 Apple Inc. and the project authors
  4. // Licensed under Apache License v2.0 with Runtime Library Exception
  5. //
  6. // See LICENSE.txt for license information:
  7. // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
  8. //
  9. // -----------------------------------------------------------------------------
  10. /// Interprets SwiftProtobuf bytecode that is a stream of "instructions" and "operands".
  11. ///
  12. /// Bytecode interpreters are generic over an `Instruction` type, which must be something (typically
  13. /// an `enum`) that has `UInt64` raw values. The only restriction on these values is that the raw
  14. /// value zero is reserved, so all instructions must have raw values of one or greater.
  15. ///
  16. /// The current version of the bytecode stream (program format 0) is represented as a `StaticString`
  17. /// where non-textual information is binary-encoded in a way that is still guaranteed to be valid
  18. /// UTF-8. Specifically,
  19. ///
  20. /// - Integers are encoded in a varint-like format similar to protobuf, except that only the low
  21. /// 7 bits are used. The most-significant bit is always clear, and the second-most-significant
  22. /// bit is used as the continuation bit.
  23. /// - Strings are length-delimited, where the length is an integer (see above) that precedes the
  24. /// string content, which is standard UTF-8. There is no null termination.
  25. /// - The stream always begins with an integer that indicates the "program format" for the stream.
  26. /// Currently, the only valid value is zero.
  27. package struct BytecodeInterpreter<Instruction: RawRepresentable>
  28. where Instruction.RawValue == UInt64 {
  29. /// The bytecode program being executed.
  30. private let program: StaticString
  31. /// Creates a new bytecode interpreter that will execute the given program.
  32. package init(program: StaticString) {
  33. self.program = program
  34. }
  35. /// Executes the program by translating its opcodes into instructions of the `Instruction` type,
  36. /// invoking the given `handleInstruction` function on each instruction until the program has
  37. /// been completely read.
  38. ///
  39. /// - Parameter handleInstruction: The function that will be invoked for each instruction that
  40. /// is read from the bytecode stream. The function takes two arguments: the `Instruction` that
  41. /// was read, and an `inout BytecodeReader` that the function should use to read operands and
  42. /// advance the stream.
  43. package func execute(handleInstruction: (Instruction, inout BytecodeReader<Instruction>) -> Void) {
  44. guard program.hasPointerRepresentation else {
  45. // The only way this could happen is if the program were a single byte, meaning that it
  46. // only has a 6-bits-or-fewer format specifier and nothing else. In other words, there
  47. // are no instructions, and we can simply return as there is nothing to execute. We
  48. // should still verify that the program format is valid, however.
  49. BytecodeReader<Instruction>.checkProgramFormat(UInt64(program.unicodeScalar.value))
  50. return
  51. }
  52. program.withUTF8Buffer { programBuffer in
  53. var reader = BytecodeReader<Instruction>(remainingProgram: programBuffer[...])
  54. while reader.hasData {
  55. let instruction = reader.nextInstruction()
  56. handleInstruction(instruction, &reader)
  57. }
  58. }
  59. }
  60. }