init dump
This commit is contained in:
20
Sources/JolkEngine/Util/FileHandle.swift
Normal file
20
Sources/JolkEngine/Util/FileHandle.swift
Normal file
@ -0,0 +1,20 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
extension FileHandle
|
||||
{
|
||||
// FixedWidthInteger or BinaryFloatingPoint
|
||||
func read<T: FixedWidthInteger>(as: T.Type) throws -> T
|
||||
{
|
||||
let size = MemoryLayout<T>.size
|
||||
guard let data = try self.read(upToCount: size)
|
||||
else { throw NSError(domain: "FileHandle.read", code: NSFileReadUnknownError, userInfo: [
|
||||
NSLocalizedFailureReasonErrorKey: "Read error",
|
||||
NSLocalizedDescriptionKey: "Read error"]) }
|
||||
guard data.count == size
|
||||
else { throw NSError(domain: "FileHandle.read", code: NSFileReadUnknownError, userInfo: [
|
||||
NSLocalizedFailureReasonErrorKey: "Value underread",
|
||||
NSLocalizedDescriptionKey: "Value underread"]) }
|
||||
return data.withUnsafeBytes { p -> T in p.load(as: T.self) }
|
||||
}
|
||||
}
|
52
Sources/JolkEngine/Util/TextFile.swift
Normal file
52
Sources/JolkEngine/Util/TextFile.swift
Normal file
@ -0,0 +1,52 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
class TextFile
|
||||
{
|
||||
private var _hnd: UnsafeMutablePointer<FILE>
|
||||
private let _maxLength: Int
|
||||
|
||||
init(fileURL: URL, maxLineLength: Int = 1024) throws
|
||||
{
|
||||
guard let f = fopen(fileURL.path, "r")
|
||||
else { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno)) }
|
||||
|
||||
_hnd = f
|
||||
_maxLength = maxLineLength
|
||||
}
|
||||
|
||||
deinit
|
||||
{
|
||||
fclose(_hnd)
|
||||
}
|
||||
|
||||
struct LinesSequence: Sequence, IteratorProtocol
|
||||
{
|
||||
typealias Element = String
|
||||
|
||||
private var _hnd: UnsafeMutablePointer<FILE>
|
||||
private var _buffer: [CChar]
|
||||
|
||||
fileprivate init(_ handle: UnsafeMutablePointer<FILE>, _ maxLength: Int)
|
||||
{
|
||||
_hnd = handle
|
||||
_buffer = [CChar](repeating: 0, count: maxLength)
|
||||
}
|
||||
|
||||
mutating func next() -> String?
|
||||
{
|
||||
guard fgets(&_buffer, Int32(_buffer.count), _hnd) != nil
|
||||
else
|
||||
{
|
||||
if feof(_hnd) != 0 { return nil }
|
||||
else { return nil }
|
||||
}
|
||||
let length = strcspn(_buffer, "\r\n")
|
||||
_buffer[length] = "\0".utf8CString[0]
|
||||
//if let pos = strchr(_buffer, Int32("\n".utf8CString[0])) { pos[0] = "\0".utf8CString[0] }
|
||||
return String(cString: _buffer)
|
||||
}
|
||||
}
|
||||
|
||||
public var lines: LinesSequence { LinesSequence(_hnd, _maxLength) }
|
||||
}
|
248
Sources/JolkEngine/Util/UBJsonReader.swift
Normal file
248
Sources/JolkEngine/Util/UBJsonReader.swift
Normal file
@ -0,0 +1,248 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
struct UBJsonReader
|
||||
{
|
||||
private var file: FileHandle
|
||||
|
||||
init(file: FileHandle)
|
||||
{
|
||||
self.file = file
|
||||
}
|
||||
|
||||
func read() throws -> UBJsonToken
|
||||
{
|
||||
guard try readCharacter() == "{"
|
||||
else { throw UBReaderError.badFormat("Stream does not start with an object") }
|
||||
return try readObject()
|
||||
}
|
||||
|
||||
private func parse(type: Character) throws -> UBJsonToken
|
||||
{
|
||||
let oldFormat = true
|
||||
return switch type
|
||||
{
|
||||
case "{": try readObject()
|
||||
case "[": try readArray()
|
||||
case "Z": .null
|
||||
case "N": .noop
|
||||
case "T": .bool(true)
|
||||
case "F": .bool(false)
|
||||
case "i": oldFormat
|
||||
? .int16(try file.read(as: Int16.self).bigEndian)
|
||||
: .int8(try file.read(as: Int8.self))
|
||||
case "U": .uint8(try file.read(as: UInt8.self))
|
||||
case "I": oldFormat
|
||||
? .int32(try file.read(as: Int32.self).bigEndian)
|
||||
: .int16(try file.read(as: Int16.self).bigEndian)
|
||||
case "l": .int32(try file.read(as: Int32.self).bigEndian)
|
||||
case "L": .int64(try file.read(as: Int64.self).bigEndian)
|
||||
case "d": .float32(try readBeFloatPiss())
|
||||
case "D": .float64(try readBeFloat())
|
||||
case "H": throw UBReaderError.badFormat("High-precision numbers are unsupported")
|
||||
case "C": .char(try readCharacter())
|
||||
case "S": .string(try readString())
|
||||
default: throw UBReaderError.badToken("Unexpected token \"\(type)\"")
|
||||
}
|
||||
}
|
||||
|
||||
private func readObject() throws -> UBJsonToken
|
||||
{
|
||||
var items = Dictionary<String, UBJsonToken>()
|
||||
while true
|
||||
{
|
||||
let type = try readCharacter()
|
||||
var name: String
|
||||
switch type
|
||||
{
|
||||
case "S", "i":
|
||||
name = try readString(type)
|
||||
case "}":
|
||||
return .object(items)
|
||||
default:
|
||||
throw UBReaderError.badToken("Unexpected token while reading object field key")
|
||||
}
|
||||
if items.keys.contains(name) { throw UBReaderError.badFormat("Object contains overlapping keys") }
|
||||
items[name] = try parse(type: try readCharacter())
|
||||
}
|
||||
}
|
||||
|
||||
private func readArray() throws -> UBJsonToken
|
||||
{
|
||||
var array = [UBJsonToken]()
|
||||
while true
|
||||
{
|
||||
let type = try readCharacter()
|
||||
switch type
|
||||
{
|
||||
case "]":
|
||||
return .array(array)
|
||||
default:
|
||||
array.append(try parse(type: type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func readBeFloatPiss() throws -> Float
|
||||
{
|
||||
guard var bytes = try? file.read(upToCount: 4)
|
||||
else { throw UBReaderError.readError("Read failure while reading float data") }
|
||||
return Float(bitPattern: bytes.withUnsafeBytes { $0.load(as: UInt32.self) }.bigEndian)
|
||||
}
|
||||
|
||||
private func readBeFloat<T: BinaryFloatingPoint, U: UnsignedInteger>() throws -> T where T.RawSignificand == U
|
||||
{
|
||||
guard var bytes = try? file.read(upToCount: MemoryLayout<U>.size)
|
||||
else { throw UBReaderError.readError("Read failure while reading float data") }
|
||||
bytes.reverse()
|
||||
return T(bytes.withUnsafeBytes { $0.load(as: U.self) })
|
||||
}
|
||||
|
||||
private func readCharacter() throws -> Character
|
||||
{
|
||||
guard let raw = try? file.read(as: UInt8.self),
|
||||
let uni = UnicodeScalar(Int(raw))
|
||||
else { throw UBReaderError.readError("Read failure while reading character") }
|
||||
return Character(uni)
|
||||
}
|
||||
|
||||
private func readString(_ optType: Character? = nil) throws -> String
|
||||
{
|
||||
let type = optType == nil ? try readCharacter() : optType!
|
||||
var length: Int
|
||||
switch type
|
||||
{
|
||||
case "S":
|
||||
guard try readCharacter() == "i"
|
||||
else { throw UBReaderError.badToken("Malformed string") }
|
||||
fallthrough
|
||||
case "i":
|
||||
length = Int(try file.read(as: Int8.self))
|
||||
case "s":
|
||||
length = Int(try file.read(as: UInt8.self))
|
||||
default: throw UBReaderError.badToken("Unexpected token while reading string")
|
||||
}
|
||||
if length < 0 { throw UBReaderError.badToken("Negative string length") }
|
||||
if length == 0 { return "" }
|
||||
guard let data = try file.read(upToCount: length)
|
||||
else { throw UBReaderError.readError("Error reading string") }
|
||||
return String(decoding: data, as: UTF8.self)
|
||||
}
|
||||
}
|
||||
|
||||
enum UBReaderError: Error
|
||||
{
|
||||
case badFormat(_ message: String)
|
||||
case badToken(_ message: String)
|
||||
case readError(_ message: String)
|
||||
case valueError(_ message: String)
|
||||
}
|
||||
|
||||
enum UBJsonToken
|
||||
{
|
||||
case object(_ fields: Dictionary<String, UBJsonToken>)
|
||||
case array(_ items: [UBJsonToken])
|
||||
case null
|
||||
case noop
|
||||
case bool(_ value: Bool)
|
||||
case int8(_ value: Int8)
|
||||
case uint8(_ value: UInt8)
|
||||
case int16(_ value: Int16)
|
||||
case int32(_ value: Int32)
|
||||
case int64(_ value: Int64)
|
||||
case highPrecision
|
||||
case float32(_ value: Float32)
|
||||
case float64(_ value: Float64)
|
||||
case char(_ value: Character)
|
||||
case string(_ value: String)
|
||||
}
|
||||
|
||||
extension UBJsonToken
|
||||
{
|
||||
var array: [UBJsonToken]
|
||||
{
|
||||
get throws
|
||||
{
|
||||
if case .array(let items) = self { return items }
|
||||
throw UBReaderError.valueError("Not an array")
|
||||
}
|
||||
}
|
||||
|
||||
func getArray(key: String) throws -> [UBJsonToken]
|
||||
{
|
||||
if case .object(let fields) = self
|
||||
{
|
||||
guard let child = fields[key]
|
||||
else { throw UBReaderError.valueError("Field \"\(key)\" not found") }
|
||||
return try child.array
|
||||
}
|
||||
throw UBReaderError.valueError("Not an object")
|
||||
}
|
||||
|
||||
var int16: Int16
|
||||
{
|
||||
get throws
|
||||
{
|
||||
if case .int16(let value) = self { return value }
|
||||
throw UBReaderError.valueError("Not an int16")
|
||||
}
|
||||
}
|
||||
|
||||
func getInt16Array(key: String) throws -> [Int16]
|
||||
{
|
||||
try getArray(key: key).map(
|
||||
{ i in
|
||||
if case .int8(let value) = i { return Int16(value) }
|
||||
else if case .uint8(let value) = i { return Int16(value) }
|
||||
else if case .int16(let value) = i { return value }
|
||||
else if case .int32(let value) = i
|
||||
{
|
||||
if value < Int16.min || value > Int16.max { throw UBReaderError.valueError("Value out of range") }
|
||||
return Int16(truncatingIfNeeded: value)
|
||||
}
|
||||
else if case .int64(let value) = i
|
||||
{
|
||||
if value < Int16.min || value > Int16.max { throw UBReaderError.valueError("Value out of range") }
|
||||
return Int16(truncatingIfNeeded: value)
|
||||
}
|
||||
else { throw UBReaderError.valueError("Can't read array as int16s") }
|
||||
})
|
||||
}
|
||||
|
||||
var float: Float
|
||||
{
|
||||
get throws
|
||||
{
|
||||
if case .float32(let value) = self { return value }
|
||||
throw UBReaderError.valueError("Not a float32")
|
||||
}
|
||||
}
|
||||
|
||||
func getFloatArray(key: String) throws -> [Float]
|
||||
{
|
||||
try getArray(key: key).map({ try $0.float })
|
||||
}
|
||||
|
||||
var string: String
|
||||
{
|
||||
get throws
|
||||
{
|
||||
if case .string(let value) = self { return value }
|
||||
throw UBReaderError.valueError("Not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func getString(key: String, default defStr: String? = nil) throws -> String
|
||||
{
|
||||
if case .object(let fields) = self
|
||||
{
|
||||
guard let child = fields[key] else
|
||||
{
|
||||
if defStr == nil { throw UBReaderError.valueError("Field \"\(key)\" not found") }
|
||||
return defStr!
|
||||
}
|
||||
return try child.string
|
||||
}
|
||||
throw UBReaderError.valueError("Not an object")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user