Skip to content
13 changes: 12 additions & 1 deletion Embedded/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ var pkgDependencies:[Package.Dependency] = [
// Metrics
//.package(url: "https://github.com/apple/swift-metrics", from: "2.5.1"),

.package(
url: "https://github.com/RandomHashTags/swift-compression",
branch: "refactor"
),

// Unlock more performance
.package(
url: "https://github.com/RandomHashTags/swift-unwrap-arithmetic-operators",
Expand Down Expand Up @@ -338,6 +343,10 @@ let traits:Set<Trait> = [
description: "Enables the design protocols."
),

.trait(
name: "Compression",
description: "Enables compression support (using swift-compression)."
),
.trait(
name: "Epoll",
description: "Enables Epoll functionality (Linux only)."
Expand Down Expand Up @@ -380,6 +389,7 @@ let package = Package(
.product(name: "MediaTypes", package: "swift-media-types", condition: .when(traits: ["MediaTypes"])),
.product(name: "UnwrapArithmeticOperators", package: "swift-unwrap-arithmetic-operators"),
.product(name: "VariableLengthArray", package: "swift-variablelengtharray"),
.product(name: "SwiftCompressionUtilities", package: "swift-compression", condition: .when(traits: ["Compression"]))
]
),

Expand Down Expand Up @@ -410,7 +420,8 @@ let package = Package(
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax")
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompression", package: "swift-compression", condition: .when(traits: ["Compression"]))
]
),

Expand Down
16 changes: 14 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ var pkgDependencies:[Package.Dependency] = [
// Media types
.package(
url: "https://github.com/RandomHashTags/swift-media-types",
from: "0.1.0",
exact: "0.1.0",
traits: ["MediaTypes", "RawValues", "FileExtensionInits", "MediaTypeParsable"]
),

// Metrics
//.package(url: "https://github.com/apple/swift-metrics", from: "2.5.1"),

.package(
url: "https://github.com/RandomHashTags/swift-compression",
branch: "refactor"
),

// Unlock more performance
.package(
url: "https://github.com/RandomHashTags/swift-unwrap-arithmetic-operators",
Expand Down Expand Up @@ -87,6 +92,7 @@ defaultTraits.formUnion([
"UnwrapArithmetic",
"Protocols",

"Compression",
"Logging",
"OpenAPI"
])
Expand Down Expand Up @@ -365,6 +371,10 @@ let traits:Set<Trait> = [
description: "Enables the design protocols."
),

.trait(
name: "Compression",
description: "Enables compression support (using swift-compression)."
),
.trait(
name: "Epoll",
description: "Enables Epoll functionality (Linux only)."
Expand Down Expand Up @@ -403,6 +413,7 @@ var targets = [
.product(name: "MediaTypes", package: "swift-media-types", condition: .when(traits: ["MediaTypes"])),
.product(name: "UnwrapArithmeticOperators", package: "swift-unwrap-arithmetic-operators"),
.product(name: "VariableLengthArray", package: "swift-variablelengtharray"),
.product(name: "SwiftCompressionUtilities", package: "swift-compression", condition: .when(traits: ["Compression"]))
]
),

Expand Down Expand Up @@ -433,7 +444,8 @@ var targets = [
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax")
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompression", package: "swift-compression", condition: .when(traits: ["Compression"]))
]
),

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Features like TLS/SSL, Web Sockets and embedded support are coming soon.
- [x] Cookies <b>(Aug 9, 2025)</b>
- [x] Header parsing <b>(Sep 9, 2025)</b>
- [x] Request body streaming <b>(Sep 10, 2025)</b>
- [x] Compression support <b>(Apr 8, 2026)</b>

### WIP

Expand All @@ -63,7 +64,6 @@ Features like TLS/SSL, Web Sockets and embedded support are coming soon.
- [ ] Cache Middleware
- [ ] Data Validation (form, POST, etc)
- [ ] Authentication
- [ ] Compression support
- [ ] OpenAPI support
- [ ] Tracing support
- [ ] TLS/SSL
Expand Down
14 changes: 14 additions & 0 deletions Sources/Destiny/extensions/Int32Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ extension Int32 {
}
}

public func writeBuffers4(
_ b1: iovec,
_ b2: iovec,
_ b3: iovec,
_ b4: iovec
) throws(DestinyError) {
let result = withUnsafePointer(to: (b1, b2, b3, b4)) {
writev(fileDescriptor, UnsafePointer<iovec>(OpaquePointer($0)), 4)
}
if result <= 0 {
throw .socketWriteFailed(errno)
}
}

public func writeBuffers4(
_ b1: iovec,
_ b2: UnsafeBufferPointer<UInt8>,
Expand Down
100 changes: 6 additions & 94 deletions Sources/Destiny/extensions/SwiftCompressionExtensions.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

/*
import SwiftCompression
import SwiftSyntax
#if Compression

import SwiftCompressionUtilities

extension CompressionAlgorithm {
public var acceptEncodingName: String {
Expand All @@ -10,100 +10,12 @@ extension CompressionAlgorithm {
case .huffmanCoding: "huffman"
case .lzw: "compress"

case .gzip: "gzip"

case ._7z: "7z"
default: rawValue
}
}
}

#if canImport(SwiftSyntax)
// MARK: SwiftSyntax
extension CompressionAlgorithm {
public static func parse(_ expr: some ExprSyntaxProtocol) -> Self? {
let key:String
guard let function = expr.functionCall else { return nil }
if let string = function.calledExpression.memberAccess?.declName.baseName.text {
key = string
} else {
return nil
}
let arguments = function.arguments
switch key {
/*case "aac": self = .aac
case "mp3": self = .mp3

case "arithmetic": self = .arithmetic
case "brotli": self = .brotli

case "bwt": self = .bwt
case "deflate": self = .deflate
case "huffmanCoding": self = .huffman(rootNode: nil)
case "json": self = .json
case "lz4": self = .lz4*/
case "lz77":
var windowSize:Int = 0, bufferSize:Int = 0, offsetBitWidth:Int = 0
for child in arguments {
switch child.label?.text {
case "windowSize": windowSize = Int(child.expression.integerLiteral!.literal.text)!
case "bufferSize": bufferSize = Int(child.expression.integerLiteral!.literal.text)!
case "offsetBitWidth": offsetBitWidth = Int(child.expression.integerLiteral!.literal.text)!
default: break
}
}
return .lz77(windowSize: windowSize, bufferSize: bufferSize, offsetBitWidth: offsetBitWidth)
/*case "lz78": self = .lz78
case "lzw": self = .lzw
case "mtf": self = .mtf*/
case "runLengthEncoding":
var minRun:Int = 0, alwaysIncludeRunCount:Bool = false
for child in arguments {
switch child.label?.text {
case "minRun": minRun = Int(child.expression.integerLiteral!.literal.text)!
case "alwaysIncludeRunCount": alwaysIncludeRunCount = child.expression.booleanLiteral!.isTrue
default: break
}
}
return .runLengthEncoding(minRun: minRun, alwaysIncludeRunCount: alwaysIncludeRunCount)
case "snappy": return CompressionAlgorithm.snappy(windowSize: 32_000)
/*case "snappyFramed": self = .snappyFramed
case "zstd": self = .zstd

case "_7z": self = ._7z
case "bzip2": self = .bzip2
case "gzip": self = .gzip
case "rar": self = .rar

case "h264": self = .h264
case "h265": self = .h265
case "jpeg": self = .jpeg
case "jpeg2000": self = .jpeg2000

case "eliasDelta": self = .eliasDelta
case "eliasGamma": self = .eliasGamma
case "eliasOmega": self = .eliasOmega
case "fibonacci": self = .fibonacci*/

case "dnaBinaryEncoding":
var baseBits:[UInt8:[Bool]] = [:]
for child in arguments {
switch child.label?.text {
case "baseBits":
child.expression.dictionary?.content.as(DictionaryElementListSyntax.self)!.forEach({
baseBits[UInt8($0.key.integerLiteral!.literal.text)!] = $0.value.array!.elements.map({ $0.expression.booleanLiteral!.isTrue })
})
default: break
}
}
return .dnaBinaryEncoding(baseBits: baseBits)
/*case "dnaSingleBlockEncoding": self = .dnaSingleBlockEncoding

case "boringSSL": self = .boringSSL

case "av1": self = .av1
case "dirac": self = .dirac
case "mpeg": self = .mpeg*/
default: return nil
}
}
}
#endif*/
#endif
15 changes: 13 additions & 2 deletions Sources/Destiny/http/HTTPResponseMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ extension HTTPResponseMessage {
status: head.status,
headers: head.headers,
body: bodyString,
contentLength: body?.count ?? 0,
contentType: contentType,
charset: charset
)
Expand Down Expand Up @@ -238,11 +239,21 @@ extension HTTPResponseMessage {
status: HTTPResponseStatus.Code,
headers: HTTPHeaders,
body: String?,
contentLength: Int,
contentType: String?,
charset: Charset?
) -> String {
let suffix = escapeLineBreak ? "\\r\\n" : "\r\n"
return create(suffix: suffix, version: version, status: status, headers: Self.headers(suffix: suffix, headers: headers), body: body, contentType: contentType, charset: charset)
return create(
suffix: suffix,
version: version,
status: status,
headers: Self.headers(suffix: suffix, headers: headers),
body: body,
contentLength: contentLength,
contentType: contentType,
charset: charset
)
}

public static func create(
Expand All @@ -251,12 +262,12 @@ extension HTTPResponseMessage {
status: HTTPResponseStatus.Code,
headers: String,
body: String?,
contentLength: Int,
contentType: String?,
charset: Charset?
) -> String {
var string = "\(version.string) \(status)\(suffix)\(headers)"
if let body {
let contentLength = body.utf8Span.count
if let contentType {
string += "content-type: \(contentType)\((charset != nil ? "; charset=" + charset!.rawName : ""))\(suffix)"
}
Expand Down
9 changes: 9 additions & 0 deletions Sources/Destiny/http/HTTPSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ extension HTTPSocket {
try fileDescriptor.writeBuffers3(b1, b2, b3)
}

public func writeBuffers4(
_ b1: iovec,
_ b2: iovec,
_ b3: iovec,
_ b4: iovec
) throws(DestinyError) {
try fileDescriptor.writeBuffers4(b1, b2, b3, b4)
}

public func writeBuffers4(
_ b1: iovec,
_ b2: UnsafeBufferPointer<UInt8>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

#if canImport(Android)
import Android
#elseif canImport(Bionic)
import Bionic
#elseif canImport(Darwin)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(WASILibc)
import WASILibc
#elseif canImport(Windows)
import Windows
#elseif canImport(WinSDK)
import WinSDK
#endif

public struct NonCopyableCompressedBody<let count: Int>: Sendable {
let bytes:[count of UInt8]

public func iovec(_ closure: (iovec) -> Void) {
bytes.span.withUnsafeBufferPointer {
closure(.init(iov_base: .init(mutating: $0.baseAddress), iov_len: $0.count))
}
}
}
Loading