swift-nio/Sources/NIOFoundationCompat/Codable+ByteBuffer.swift

140 lines
6.4 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2019-2021 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import NIOCore
import Foundation
extension ByteBuffer {
/// Attempts to decode the `length` bytes from `index` using the `JSONDecoder` `decoder` as `T`.
///
/// - parameters:
/// - type: The type type that is attempted to be decoded.
/// - decoder: The `JSONDecoder` that is used for the decoding.
/// - index: The index of the first byte to decode.
/// - length: The number of bytes to decode.
/// - returns: The decoded value if successful or `nil` if there are not enough readable bytes available.
@inlinable
public func getJSONDecodable<T: Decodable>(_ type: T.Type,
decoder: JSONDecoder = JSONDecoder(),
at index: Int, length: Int) throws -> T? {
guard let data = self.getData(at: index, length: length, byteTransferStrategy: .noCopy) else {
return nil
}
return try decoder.decode(T.self, from: data)
}
/// Reads `length` bytes from this `ByteBuffer` and then attempts to decode them using the `JSONDecoder` `decoder`.
///
/// - parameters:
/// - type: The type type that is attempted to be decoded.
/// - decoder: The `JSONDecoder` that is used for the decoding.
/// - length: The number of bytes to decode.
/// - returns: The decoded value is successful or `nil` if there are not enough readable bytes available.
@inlinable
public mutating func readJSONDecodable<T: Decodable>(_ type: T.Type,
decoder: JSONDecoder = JSONDecoder(),
length: Int) throws -> T? {
guard let decoded = try self.getJSONDecodable(T.self,
decoder: decoder,
at: self.readerIndex,
length: length) else {
return nil
}
self.moveReaderIndex(forwardBy: length)
return decoded
}
/// Encodes `value` using the `JSONEncoder` `encoder` and set the resulting bytes into this `ByteBuffer` at the
/// given `index`.
///
/// - note: The `writerIndex` remains unchanged.
///
/// - parameters:
/// - value: An `Encodable` value to encode.
/// - encoder: The `JSONEncoder` to encode `value` with.
/// - returns: The number of bytes written.
@inlinable
@discardableResult
public mutating func setJSONEncodable<T: Encodable>(_ value: T,
encoder: JSONEncoder = JSONEncoder(),
at index: Int) throws -> Int {
let data = try encoder.encode(value)
return self.setBytes(data, at: index)
}
/// Encodes `value` using the `JSONEncoder` `encoder` and writes the resulting bytes into this `ByteBuffer`.
///
/// If successful, this will move the writer index forward by the number of bytes written.
///
/// - parameters:
/// - value: An `Encodable` value to encode.
/// - encoder: The `JSONEncoder` to encode `value` with.
/// - returns: The number of bytes written.
@inlinable
@discardableResult
public mutating func writeJSONEncodable<T: Encodable>(_ value: T,
encoder: JSONEncoder = JSONEncoder()) throws -> Int {
let result = try self.setJSONEncodable(value, encoder: encoder, at: self.writerIndex)
self.moveWriterIndex(forwardBy: result)
return result
}
}
extension JSONDecoder {
/// Returns a value of the type you specify, decoded from a JSON object inside the readable bytes of a `ByteBuffer`.
///
/// If the `ByteBuffer` does not contain valid JSON, this method throws the
/// `DecodingError.dataCorrupted(_:)` error. If a value within the JSON
/// fails to decode, this method throws the corresponding error.
///
/// - note: The provided `ByteBuffer` remains unchanged, neither the `readerIndex` nor the `writerIndex` will move.
/// If you would like the `readerIndex` to move, consider using `ByteBuffer.readJSONDecodable(_:length:)`.
///
/// - parameters:
/// - type: The type of the value to decode from the supplied JSON object.
/// - buffer: The `ByteBuffer` that contains JSON object to decode.
/// - returns: The decoded object.
public func decode<T: Decodable>(_ type: T.Type, from buffer: ByteBuffer) throws -> T {
return try buffer.getJSONDecodable(T.self,
decoder: self,
at: buffer.readerIndex,
length: buffer.readableBytes)! // must work, enough readable bytes
}
}
extension JSONEncoder {
/// Writes a JSON-encoded representation of the value you supply into the supplied `ByteBuffer`.
///
/// - parameters:
/// - value: The value to encode as JSON.
/// - buffer: The `ByteBuffer` to encode into.
public func encode<T: Encodable>(_ value: T,
into buffer: inout ByteBuffer) throws {
try buffer.writeJSONEncodable(value, encoder: self)
}
/// Writes a JSON-encoded representation of the value you supply into a `ByteBuffer` that is freshly allocated.
///
/// - parameters:
/// - value: The value to encode as JSON.
/// - allocator: The `ByteBufferAllocator` which is used to allocate the `ByteBuffer` to be returned.
/// - returns: The `ByteBuffer` containing the encoded JSON.
public func encodeAsByteBuffer<T: Encodable>(_ value: T, allocator: ByteBufferAllocator) throws -> ByteBuffer {
let data = try self.encode(value)
var buffer = allocator.buffer(capacity: data.count)
buffer.writeBytes(data)
return buffer
}
}