Skip to content

Commit b41810e

Browse files
authored
Merge branch 'swift-server:main' into sebsto/awssdk_example
2 parents 6bf8f0f + 053de48 commit b41810e

File tree

4 files changed

+275
-9
lines changed

4 files changed

+275
-9
lines changed

Package.swift

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ let package = Package(
3232
dependencies: [
3333
.byName(name: "AWSLambdaRuntimeCore"),
3434
.product(name: "NIOCore", package: "swift-nio"),
35-
.product(name: "NIOFoundationCompat", package: "swift-nio"),
3635
]
3736
),
3837
.target(

Sources/AWSLambdaRuntime/Lambda+Codable.swift

+25-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
@_exported import AWSLambdaRuntimeCore
1616
import NIOCore
17-
import NIOFoundationCompat
1817

1918
#if canImport(FoundationEssentials)
2019
import FoundationEssentials
@@ -24,7 +23,25 @@ import class Foundation.JSONDecoder
2423
import class Foundation.JSONEncoder
2524
#endif
2625

27-
extension JSONDecoder: AWSLambdaRuntimeCore.LambdaEventDecoder {}
26+
public struct LambdaJSONEventDecoder: LambdaEventDecoder {
27+
@usableFromInline let jsonDecoder: JSONDecoder
28+
29+
@inlinable
30+
public init(_ jsonDecoder: JSONDecoder) {
31+
self.jsonDecoder = jsonDecoder
32+
}
33+
34+
@inlinable
35+
public func decode<Event>(_ type: Event.Type, from buffer: NIOCore.ByteBuffer) throws -> Event
36+
where Event: Decodable {
37+
try buffer.getJSONDecodable(
38+
Event.self,
39+
decoder: self.jsonDecoder,
40+
at: buffer.readerIndex,
41+
length: buffer.readableBytes
42+
)! // must work, enough readable bytes
43+
}
44+
}
2845

2946
public struct LambdaJSONOutputEncoder<Output: Encodable>: LambdaOutputEncoder {
3047
@usableFromInline let jsonEncoder: JSONEncoder
@@ -36,7 +53,7 @@ public struct LambdaJSONOutputEncoder<Output: Encodable>: LambdaOutputEncoder {
3653

3754
@inlinable
3855
public func encode(_ value: Output, into buffer: inout ByteBuffer) throws {
39-
try self.jsonEncoder.encode(value, into: &buffer)
56+
try buffer.writeJSONEncodable(value, encoder: self.jsonEncoder)
4057
}
4158
}
4259

@@ -55,11 +72,11 @@ extension LambdaCodableAdapter {
5572
Output: Encodable,
5673
Output == Handler.Output,
5774
Encoder == LambdaJSONOutputEncoder<Output>,
58-
Decoder == JSONDecoder
75+
Decoder == LambdaJSONEventDecoder
5976
{
6077
self.init(
6178
encoder: LambdaJSONOutputEncoder(encoder),
62-
decoder: decoder,
79+
decoder: LambdaJSONEventDecoder(decoder),
6380
handler: handler
6481
)
6582
}
@@ -81,7 +98,7 @@ extension LambdaRuntime {
8198
LambdaHandlerAdapter<Event, Output, ClosureHandler<Event, Output>>,
8299
Event,
83100
Output,
84-
JSONDecoder,
101+
LambdaJSONEventDecoder,
85102
LambdaJSONOutputEncoder<Output>
86103
>
87104
{
@@ -106,12 +123,12 @@ extension LambdaRuntime {
106123
LambdaHandlerAdapter<Event, Void, ClosureHandler<Event, Void>>,
107124
Event,
108125
Void,
109-
JSONDecoder,
126+
LambdaJSONEventDecoder,
110127
VoidEncoder
111128
>
112129
{
113130
let handler = LambdaCodableAdapter(
114-
decoder: decoder,
131+
decoder: LambdaJSONEventDecoder(decoder),
115132
handler: LambdaHandlerAdapter(handler: ClosureHandler(body: body))
116133
)
117134

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
//===----------------------------------------------------------------------===//
16+
//
17+
// This source file is part of the SwiftNIO open source project
18+
//
19+
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
20+
// Licensed under Apache License v2.0
21+
//
22+
// See LICENSE.txt for license information
23+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
24+
//
25+
// SPDX-License-Identifier: Apache-2.0
26+
//
27+
//===----------------------------------------------------------------------===//
28+
29+
import Foundation
30+
import NIOCore
31+
32+
// This is NIO's `NIOFoundationCompat` module which at the moment only adds `ByteBuffer` utility methods
33+
// for Foundation's `Data` type.
34+
//
35+
// The reason that it's not in the `NIO` module is that we don't want to have any direct Foundation dependencies
36+
// in `NIO` as Foundation is problematic for a few reasons:
37+
//
38+
// - its implementation is different on Linux and on macOS which means our macOS tests might be inaccurate
39+
// - on macOS Foundation is mostly written in ObjC which means the autorelease pool might get populated
40+
// - `swift-corelibs-foundation` (the OSS Foundation used on Linux) links the world which will prevent anyone from
41+
// having static binaries. It can also cause problems in the choice of an SSL library as Foundation already brings
42+
// the platforms OpenSSL in which might cause problems.
43+
44+
extension ByteBuffer {
45+
/// Controls how bytes are transferred between `ByteBuffer` and other storage types.
46+
@usableFromInline
47+
enum ByteTransferStrategy: Sendable {
48+
/// Force a copy of the bytes.
49+
case copy
50+
51+
/// Do not copy the bytes if at all possible.
52+
case noCopy
53+
54+
/// Use a heuristic to decide whether to copy the bytes or not.
55+
case automatic
56+
}
57+
58+
// MARK: - Data APIs
59+
60+
/// Return `length` bytes starting at `index` and return the result as `Data`. This will not change the reader index.
61+
/// The selected bytes must be readable or else `nil` will be returned.
62+
///
63+
/// - parameters:
64+
/// - index: The starting index of the bytes of interest into the `ByteBuffer`
65+
/// - length: The number of bytes of interest
66+
/// - byteTransferStrategy: Controls how to transfer the bytes. See `ByteTransferStrategy` for an explanation
67+
/// of the options.
68+
/// - returns: A `Data` value containing the bytes of interest or `nil` if the selected bytes are not readable.
69+
@usableFromInline
70+
func getData(at index0: Int, length: Int, byteTransferStrategy: ByteTransferStrategy) -> Data? {
71+
let index = index0 - self.readerIndex
72+
guard index >= 0 && length >= 0 && index <= self.readableBytes - length else {
73+
return nil
74+
}
75+
let doCopy: Bool
76+
switch byteTransferStrategy {
77+
case .copy:
78+
doCopy = true
79+
case .noCopy:
80+
doCopy = false
81+
case .automatic:
82+
doCopy = length <= 256 * 1024
83+
}
84+
85+
return self.withUnsafeReadableBytesWithStorageManagement { ptr, storageRef in
86+
if doCopy {
87+
return Data(
88+
bytes: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
89+
count: Int(length)
90+
)
91+
} else {
92+
_ = storageRef.retain()
93+
return Data(
94+
bytesNoCopy: UnsafeMutableRawPointer(mutating: ptr.baseAddress!.advanced(by: index)),
95+
count: Int(length),
96+
deallocator: .custom { _, _ in storageRef.release() }
97+
)
98+
}
99+
}
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
//===----------------------------------------------------------------------===//
16+
//
17+
// This source file is part of the SwiftNIO open source project
18+
//
19+
// Copyright (c) 2019-2021 Apple Inc. and the SwiftNIO project authors
20+
// Licensed under Apache License v2.0
21+
//
22+
// See LICENSE.txt for license information
23+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
24+
//
25+
// SPDX-License-Identifier: Apache-2.0
26+
//
27+
//===----------------------------------------------------------------------===//
28+
29+
import NIOCore
30+
31+
#if canImport(FoundationEssentials)
32+
import FoundationEssentials
33+
#else
34+
import Foundation
35+
#endif
36+
37+
extension ByteBuffer {
38+
/// Attempts to decode the `length` bytes from `index` using the `JSONDecoder` `decoder` as `T`.
39+
///
40+
/// - parameters:
41+
/// - type: The type type that is attempted to be decoded.
42+
/// - decoder: The `JSONDecoder` that is used for the decoding.
43+
/// - index: The index of the first byte to decode.
44+
/// - length: The number of bytes to decode.
45+
/// - returns: The decoded value if successful or `nil` if there are not enough readable bytes available.
46+
@inlinable
47+
func getJSONDecodable<T: Decodable>(
48+
_ type: T.Type,
49+
decoder: JSONDecoder = JSONDecoder(),
50+
at index: Int,
51+
length: Int
52+
) throws -> T? {
53+
guard let data = self.getData(at: index, length: length, byteTransferStrategy: .noCopy) else {
54+
return nil
55+
}
56+
return try decoder.decode(T.self, from: data)
57+
}
58+
59+
/// Encodes `value` using the `JSONEncoder` `encoder` and set the resulting bytes into this `ByteBuffer` at the
60+
/// given `index`.
61+
///
62+
/// - note: The `writerIndex` remains unchanged.
63+
///
64+
/// - parameters:
65+
/// - value: An `Encodable` value to encode.
66+
/// - encoder: The `JSONEncoder` to encode `value` with.
67+
/// - returns: The number of bytes written.
68+
@inlinable
69+
@discardableResult
70+
mutating func setJSONEncodable<T: Encodable>(
71+
_ value: T,
72+
encoder: JSONEncoder = JSONEncoder(),
73+
at index: Int
74+
) throws -> Int {
75+
let data = try encoder.encode(value)
76+
return self.setBytes(data, at: index)
77+
}
78+
79+
/// Encodes `value` using the `JSONEncoder` `encoder` and writes the resulting bytes into this `ByteBuffer`.
80+
///
81+
/// If successful, this will move the writer index forward by the number of bytes written.
82+
///
83+
/// - parameters:
84+
/// - value: An `Encodable` value to encode.
85+
/// - encoder: The `JSONEncoder` to encode `value` with.
86+
/// - returns: The number of bytes written.
87+
@inlinable
88+
@discardableResult
89+
mutating func writeJSONEncodable<T: Encodable>(
90+
_ value: T,
91+
encoder: JSONEncoder = JSONEncoder()
92+
) throws -> Int {
93+
let result = try self.setJSONEncodable(value, encoder: encoder, at: self.writerIndex)
94+
self.moveWriterIndex(forwardBy: result)
95+
return result
96+
}
97+
}
98+
99+
extension JSONDecoder {
100+
/// Returns a value of the type you specify, decoded from a JSON object inside the readable bytes of a `ByteBuffer`.
101+
///
102+
/// If the `ByteBuffer` does not contain valid JSON, this method throws the
103+
/// `DecodingError.dataCorrupted(_:)` error. If a value within the JSON
104+
/// fails to decode, this method throws the corresponding error.
105+
///
106+
/// - note: The provided `ByteBuffer` remains unchanged, neither the `readerIndex` nor the `writerIndex` will move.
107+
/// If you would like the `readerIndex` to move, consider using `ByteBuffer.readJSONDecodable(_:length:)`.
108+
///
109+
/// - parameters:
110+
/// - type: The type of the value to decode from the supplied JSON object.
111+
/// - buffer: The `ByteBuffer` that contains JSON object to decode.
112+
/// - returns: The decoded object.
113+
func decode<T: Decodable>(_ type: T.Type, from buffer: ByteBuffer) throws -> T {
114+
try buffer.getJSONDecodable(
115+
T.self,
116+
decoder: self,
117+
at: buffer.readerIndex,
118+
length: buffer.readableBytes
119+
)! // must work, enough readable bytes// must work, enough readable bytes
120+
}
121+
}
122+
123+
extension JSONEncoder {
124+
/// Writes a JSON-encoded representation of the value you supply into the supplied `ByteBuffer`.
125+
///
126+
/// - parameters:
127+
/// - value: The value to encode as JSON.
128+
/// - buffer: The `ByteBuffer` to encode into.
129+
@inlinable
130+
func encode<T: Encodable>(
131+
_ value: T,
132+
into buffer: inout ByteBuffer
133+
) throws {
134+
try buffer.writeJSONEncodable(value, encoder: self)
135+
}
136+
137+
/// Writes a JSON-encoded representation of the value you supply into a `ByteBuffer` that is freshly allocated.
138+
///
139+
/// - parameters:
140+
/// - value: The value to encode as JSON.
141+
/// - allocator: The `ByteBufferAllocator` which is used to allocate the `ByteBuffer` to be returned.
142+
/// - returns: The `ByteBuffer` containing the encoded JSON.
143+
func encodeAsByteBuffer<T: Encodable>(_ value: T, allocator: ByteBufferAllocator) throws -> ByteBuffer {
144+
let data = try self.encode(value)
145+
var buffer = allocator.buffer(capacity: data.count)
146+
buffer.writeBytes(data)
147+
return buffer
148+
}
149+
}

0 commit comments

Comments
 (0)