mirror of https://github.com/apple/pkl-swift
358 lines
12 KiB
Swift
358 lines
12 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Foundation
|
|
|
|
extension _MessagePackEncoder {
|
|
final class SingleValueContainer {
|
|
fileprivate var canEncodeNewValue = true
|
|
fileprivate func checkCanEncode(value: Any?) throws {
|
|
guard self.canEncodeNewValue else {
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath,
|
|
debugDescription:
|
|
"Attempt to encode value through single value container when previously value already encoded."
|
|
)
|
|
throw EncodingError.invalidValue(value as Any, context)
|
|
}
|
|
}
|
|
|
|
var codingPath: [CodingKey]
|
|
var userInfo: [CodingUserInfoKey: Any]
|
|
private var storage: [UInt8] = []
|
|
|
|
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any]) {
|
|
self.codingPath = codingPath
|
|
self.userInfo = userInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _MessagePackEncoder.SingleValueContainer: SingleValueEncodingContainer {
|
|
func encodeNil() throws {
|
|
try checkCanEncode(value: nil)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xC0)
|
|
}
|
|
|
|
func encode(_ value: Bool) throws {
|
|
try checkCanEncode(value: nil)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
switch value {
|
|
case false:
|
|
self.storage.append(0xC2)
|
|
case true:
|
|
self.storage.append(0xC3)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: String) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
guard let data = value.data(using: .utf8) else {
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath, debugDescription: "Cannot encode string using UTF-8 encoding."
|
|
)
|
|
throw EncodingError.invalidValue(value, context)
|
|
}
|
|
let bytes = [UInt8](data)
|
|
|
|
let length = bytes.count
|
|
if let uint8 = UInt8(exactly: length) {
|
|
if uint8 <= 31 {
|
|
self.storage.append(0xA0 + uint8)
|
|
} else {
|
|
self.storage.append(0xD9)
|
|
self.storage.append(contentsOf: uint8.bytes)
|
|
}
|
|
} else if let uint16 = UInt16(exactly: length) {
|
|
self.storage.append(0xDA)
|
|
self.storage.append(contentsOf: uint16.bytes)
|
|
} else if let uint32 = UInt32(exactly: length) {
|
|
self.storage.append(0xDB)
|
|
self.storage.append(contentsOf: uint32.bytes)
|
|
} else {
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath, debugDescription: "Cannot encode string with length \(length)."
|
|
)
|
|
throw EncodingError.invalidValue(value, context)
|
|
}
|
|
|
|
self.storage.append(contentsOf: bytes)
|
|
}
|
|
|
|
func encode(_ value: Double) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xCB)
|
|
self.storage.append(contentsOf: value.bitPattern.bytes)
|
|
}
|
|
|
|
func encode(_ value: Float) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xCA)
|
|
self.storage.append(contentsOf: value.bitPattern.bytes)
|
|
}
|
|
|
|
func encode(_ value: some BinaryInteger & Encodable) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
if value < 0 {
|
|
if let int8 = Int8(exactly: value) {
|
|
return try self.encode(int8)
|
|
} else if let int16 = Int16(exactly: value) {
|
|
return try self.encode(int16)
|
|
} else if let int32 = Int32(exactly: value) {
|
|
return try self.encode(int32)
|
|
} else if let int64 = Int64(exactly: value) {
|
|
return try self.encode(int64)
|
|
}
|
|
} else {
|
|
if let uint8 = UInt8(exactly: value) {
|
|
return try self.encode(uint8)
|
|
} else if let uint16 = UInt16(exactly: value) {
|
|
return try self.encode(uint16)
|
|
} else if let uint32 = UInt32(exactly: value) {
|
|
return try self.encode(uint32)
|
|
} else if let uint64 = UInt64(exactly: value) {
|
|
return try self.encode(uint64)
|
|
}
|
|
}
|
|
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath, debugDescription: "Cannot encode integer \(value)."
|
|
)
|
|
throw EncodingError.invalidValue(value, context)
|
|
}
|
|
|
|
func encode(_ value: Int8) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
if value >= 0, value <= 127 {
|
|
self.storage.append(UInt8(value))
|
|
} else if value < 0, value >= -31 {
|
|
self.storage.append(0xE0 + (0x1F & UInt8(truncatingIfNeeded: value)))
|
|
} else {
|
|
self.storage.append(0xD0)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: Int16) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xD1)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: Int32) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xD2)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: Int64) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xD3)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: UInt8) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
if value <= 127 {
|
|
self.storage.append(value)
|
|
} else {
|
|
self.storage.append(0xCC)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: UInt16) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xCD)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: UInt32) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xCE)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: UInt64) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
self.storage.append(0xCF)
|
|
self.storage.append(contentsOf: value.bytes)
|
|
}
|
|
|
|
func encode(_ value: Date) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
|
|
let timeInterval = value.timeIntervalSince1970
|
|
let (integral, fractional) = modf(timeInterval)
|
|
|
|
let seconds = Int64(integral)
|
|
let nanoseconds = UInt32(fractional * Double(NSEC_PER_SEC))
|
|
|
|
if seconds < 0 || seconds > UInt32.max {
|
|
self.storage.append(0xC7)
|
|
self.storage.append(0x0C)
|
|
self.storage.append(0xFF)
|
|
self.storage.append(contentsOf: nanoseconds.bytes)
|
|
self.storage.append(contentsOf: seconds.bytes)
|
|
} else if nanoseconds > 0 {
|
|
self.storage.append(0xD7)
|
|
self.storage.append(0xFF)
|
|
self.storage.append(contentsOf: ((UInt64(nanoseconds) << 34) + UInt64(seconds)).bytes)
|
|
} else {
|
|
self.storage.append(0xD6)
|
|
self.storage.append(0xFF)
|
|
self.storage.append(contentsOf: UInt32(seconds).bytes)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: Data) throws {
|
|
let length = value.count
|
|
if let uint8 = UInt8(exactly: length) {
|
|
self.storage.append(0xC4)
|
|
self.storage.append(uint8)
|
|
self.storage.append(contentsOf: value)
|
|
} else if let uint16 = UInt16(exactly: length) {
|
|
self.storage.append(0xC5)
|
|
self.storage.append(contentsOf: uint16.bytes)
|
|
self.storage.append(contentsOf: value)
|
|
} else if let uint32 = UInt32(exactly: length) {
|
|
self.storage.append(0xC6)
|
|
self.storage.append(contentsOf: uint32.bytes)
|
|
self.storage.append(contentsOf: value)
|
|
} else {
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath,
|
|
debugDescription: "Cannot encode data of length \(value.count)."
|
|
)
|
|
throw EncodingError.invalidValue(value, context)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: [UInt8]) throws {
|
|
let length = value.count
|
|
if let uint8 = UInt8(exactly: length) {
|
|
self.storage.append(0xC4)
|
|
self.storage.append(uint8)
|
|
self.storage.append(contentsOf: value)
|
|
} else if let uint16 = UInt16(exactly: length) {
|
|
self.storage.append(0xC5)
|
|
self.storage.append(contentsOf: uint16.bytes)
|
|
self.storage.append(contentsOf: value)
|
|
} else if let uint32 = UInt32(exactly: length) {
|
|
self.storage.append(0xC6)
|
|
self.storage.append(contentsOf: uint32.bytes)
|
|
self.storage.append(contentsOf: value)
|
|
} else {
|
|
let context = EncodingError.Context(
|
|
codingPath: self.codingPath,
|
|
debugDescription: "Cannot encode data of length \(value.count)."
|
|
)
|
|
throw EncodingError.invalidValue(value, context)
|
|
}
|
|
}
|
|
|
|
func encode(_ value: URL) throws {
|
|
try self.encode("\(value)")
|
|
}
|
|
|
|
func encode(_ value: some Encodable) throws {
|
|
try checkCanEncode(value: value)
|
|
defer { self.canEncodeNewValue = false }
|
|
switch value {
|
|
case let data as Data:
|
|
try self.encode(data)
|
|
case let date as Date:
|
|
try self.encode(date)
|
|
case let url as URL:
|
|
try self.encode(url)
|
|
case let bool as Bool:
|
|
try self.encode(bool)
|
|
case let str as String:
|
|
try self.encode(str)
|
|
case let double as Double:
|
|
try self.encode(double)
|
|
case let float as Float:
|
|
try self.encode(float)
|
|
case let int8 as Int8:
|
|
try self.encode(int8)
|
|
case let int16 as Int16:
|
|
try self.encode(int16)
|
|
case let int32 as Int32:
|
|
try self.encode(int32)
|
|
case let int64 as Int64:
|
|
try self.encode(int64)
|
|
case let uint8 as UInt8:
|
|
try self.encode(uint8)
|
|
case let uint16 as UInt16:
|
|
try self.encode(uint16)
|
|
case let uint32 as UInt32:
|
|
try self.encode(uint32)
|
|
case let uint64 as UInt64:
|
|
try self.encode(uint64)
|
|
case let num as any BinaryInteger & Encodable:
|
|
try self.encode(num)
|
|
case let bytes as [UInt8]:
|
|
guard type(of: value) == [UInt8].self else {
|
|
fallthrough
|
|
}
|
|
try self.encode(bytes)
|
|
default:
|
|
let writer: Writer = BufferWriter()
|
|
let encoder = _MessagePackEncoder()
|
|
encoder.userInfo = userInfo
|
|
encoder.codingPath = codingPath
|
|
try value.encode(to: encoder)
|
|
try encoder.write(into: writer)
|
|
let data = (writer as! BufferWriter).bytes
|
|
self.storage.append(contentsOf: data)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _MessagePackEncoder.SingleValueContainer: _MessagePackEncodingContainer {
|
|
func write(into: Writer) throws {
|
|
_ = try storage.withUnsafeBytes { try into.write($0) }
|
|
}
|
|
}
|