Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,16 @@ let package = Package(
.package(
url:
"https://github.com/swiftlang/swift-tools-protocols",
revision: "f86b413a803761408e5e718912c46d75a340801b"
revision: "88612c51de4cbf636a6b948c64ea5ebd55b8a0ad"
),

.package(
url: "https://github.com/apple/swift-argument-parser",
revision: "1.5.0"
),
.package(
url: "https://github.com/swiftlang/sourcekit-lsp",
revision: "0e061c5c1075152bc2e6187679a11b81d0c3e326" // latest main commit November 29, 2025
// TODO: Ideally it would be better to upstream these changes to sourceKit-lsp
// url: "https://github.com/rockbruno/sourcekit-lsp",
// revision: "c052baae81ec6532bb2f939a21acc4650fb1dc86"
),
.package(
url: "https://github.com/apple/swift-protobuf.git",
revision: "102a647b573f60f73afdce5613a51d71349fe507"
revision: "1.33.3"
),
],
targets: [
Expand Down Expand Up @@ -78,7 +71,7 @@ let package = Package(
],
resources: [
.copy("Resources/aquery.pb"),
.copy("Resources/streamdeps.pb"),
.copy("Resources/cquery.pb"),
],
),
.target(
Expand All @@ -98,7 +91,6 @@ let package = Package(
dependencies: ["BazelProtobufBindings"],
resources: [
.copy("Resources/actions.pb"),
.copy("Resources/streamdeps.pb"),
],
),
]
Expand Down
109 changes: 2 additions & 107 deletions Sources/BazelProtobufBindings/BazelProtobufBindings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,116 +20,11 @@
import Foundation

package enum BazelProtobufBindings {
package static func parseQueryTargets(data: Data) throws -> [BlazeQuery_Target] {
var targets: [BlazeQuery_Target] = []
let messages = try parseMultipleDelimitedMessages(from: data)
for message in messages {
let target = try BlazeQuery_Target(serializedBytes: message)
targets.append(target)
}

return targets
}

package static func parseActionGraph(data: Data) throws -> Analysis_ActionGraphContainer {
try Analysis_ActionGraphContainer(serializedBytes: data)
}
}

extension BazelProtobufBindings {
/// Bazel query outputs a series of messages and each one is prefixed with length to indicate
/// the number of bytes in the payload. Returns a tuple of (value, bytesConsumed).
/// Protobuf [documentation](https://protobuf.dev/programming-guides/encoding/) provides more
/// details on how `varint` works.
private static func parseVarint(
from data: Data,
startIndex: Int
) throws -> (UInt64, Int) {
guard startIndex < data.count else {
throw VarintError.truncated
}

var result: UInt64 = 0
var shift = 0
var bytesRead = 0
var index = startIndex

while index < data.count {
let byte = data[index]
bytesRead += 1
index += 1

// Check for overflow (varints can be at most 10 bytes for 64-bit values)
if bytesRead > 10 {
throw VarintError.overflow
}

// Extract the 7 data bits
let dataBits = UInt64(byte & 0x7F)

// Check for shift overflow
if shift >= 64 {
throw VarintError.overflow
}

// little-endian -> big-endian
result |= dataBits << shift

// If the continuation bit (MSB) is not set, we're done
if (byte & 0x80) == 0 {
return (result, bytesRead)
}

shift += 7
}

// If we get here, the varint was truncated
throw VarintError.truncated
package static func parseCqueryResult(data: Data) throws -> Analysis_CqueryResult {
try Analysis_CqueryResult(serializedBytes: data)
}

/// Parse the length prefix and return the message data
private static func parseDelimitedMessage(
from data: Data,
startIndex: Int = 0
) throws -> (Data, Int) {
let (messageLength, lengthBytes) = try parseVarint(
from: data,
startIndex: startIndex
)

let messageStart = startIndex + lengthBytes
let messageEnd = messageStart + Int(messageLength)

guard messageEnd <= data.count else {
throw VarintError.truncated
}

let messageData = data.subdata(in: messageStart..<messageEnd)
let totalBytesConsumed = lengthBytes + Int(messageLength)

return (messageData, totalBytesConsumed)
}

/// Parse multiple delimited messages from a data stream
private static func parseMultipleDelimitedMessages(from data: Data) throws -> [Data] {
var messages: [Data] = []
var currentIndex = 0

while currentIndex < data.count {
let (messageData, bytesConsumed) = try parseDelimitedMessage(
from: data,
startIndex: currentIndex
)
messages.append(messageData)
currentIndex += bytesConsumed
}

return messages
}
}

package enum VarintError: Error {
case truncated
case overflow
case invalidData
}
Loading