Skip to content

Commit

Permalink
fix: Remove legacy reader and cleanup (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrick-zippenfenig authored Jan 27, 2025
1 parent 6a0e399 commit 716b034
Show file tree
Hide file tree
Showing 16 changed files with 869 additions and 1,063 deletions.
106 changes: 0 additions & 106 deletions Swift/OmFileFormat/Backend.swift

This file was deleted.

109 changes: 3 additions & 106 deletions Swift/OmFileFormat/FileExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import Foundation
extension FileHandle {
/// Create new file and convert it into a `FileHandle`. For some reason this does not exist in stock swift....
/// Error on existing file
public static func createNewFile(file: String, size: Int? = nil, sparseSize: Int? = nil, exclusive: Bool = false) throws -> FileHandle {
public static func createNewFile(file: String, size: Int? = nil, sparseSize: Int? = nil, overwrite: Bool = false) throws -> FileHandle {
let flagOverwrite = overwrite ? O_TRUNC : O_EXCL
let flags = O_RDWR | O_CREAT | flagOverwrite
// 0644 permissions
// O_TRUNC for overwrite
// O_EXCL exclusive access
let flags = exclusive ? (O_RDWR | O_CREAT | O_EXCL) : (O_RDWR | O_CREAT)
let fn = open(file, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
guard fn > 0 else {
let error = String(cString: strerror(errno))
Expand Down Expand Up @@ -80,107 +79,5 @@ extension FileHandle {
let handle = FileHandle(fileDescriptor: fn, closeOnDealloc: true)
return handle
}

/// Check if the file was deleted on the file system. Linux keep the file alive, as long as some processes have it open.
public func wasDeleted() -> Bool {
// This field contains the number of hard links to the file.
return fileStats().st_nlink == 0
}

public func fileSize() -> Int {
return Int(fileStats().st_size)
}

public func fileSizeAndModificationTime() -> (size: Int, modificationTime: Date, creationTime: Date) {
let stats = fileStats()
return (Int(stats.st_size), stats.modificationTime, stats.creationTime)
}

/// Return file `stat` structure
public func fileStats() -> stat {
var stats = stat()
guard fstat(fileDescriptor, &stats) != -1 else {
let error = String(cString: strerror(errno))
fatalError("fstat failed on open file descriptor. Error \(errno) \(error)")
}
return stats
}
}

extension FileManager {
/// Rename file and replace if `to` already exists. https://www.gnu.org/software/libc/manual/html_node/Renaming-Files.html
public func moveFileOverwrite(from: String, to: String) throws {
guard rename(from, to) != -1 else {
let error = String(cString: strerror(errno))
throw OmFileFormatSwiftError.cannotMoveFile(from: from, to: to, errno: errno, error: error)
}
}

public func removeItemIfExists(at: String) throws {
if fileExists(atPath: at) {
try removeItem(atPath: at)
}
}

/// Return file `stat` structure
public func fileStats(at: String) -> stat? {
var stats = stat()
let ret = stat(at, &stats)
guard ret != -1 else {
if errno == 2 {
// No such file or directory
return nil
}
let error = String(cString: strerror(errno))
fatalError("fstat failed on open file descriptor. Error \(errno) \(error), ret=\(ret)")
}
return stats
}

/// Get modification and creation time
public func fileSizeAndModificationTime(at: String) -> (size: Int, modificationTime: Date, creationTime: Date)? {
guard let stats = fileStats(at: at) else {
return nil
}
return (Int(stats.st_size), stats.modificationTime, stats.creationTime)
}

/// Wait until the file was not updated for at least 60 seconds. If the file does not exist, do nothing
public func waitIfFileWasRecentlyModified(at: String, waitTimeMinutes: Int = 15) {
// Wait up to 15 minutes
for _ in 0 ..< (waitTimeMinutes*6) {
guard let mTime = FileManager.default.fileStats(at: at)?.modificationTime,
mTime > Date().addingTimeInterval(-60) else {
break
}
print("Another process is writing to \(at). Check in 10s. Waiting up to \(waitTimeMinutes) minutes.")
sleep(10)
}
}
}

extension stat {
/// Last modification time of the file
public var modificationTime: Date {
#if os(Linux)
let seconds = Double(st_mtim.tv_sec)
let nanosends = Double(st_mtim.tv_nsec)
#else
let seconds = Double(st_mtimespec.tv_sec)
let nanosends = Double(st_mtimespec.tv_nsec)
#endif
return Date(timeIntervalSince1970: seconds + nanosends / 1_000_000)
}

/// Creation time of the file / inode
public var creationTime: Date {
#if os(Linux)
let seconds = Double(st_ctim.tv_sec)
let nanosends = Double(st_ctim.tv_nsec)
#else
let seconds = Double(st_ctimespec.tv_sec)
let nanosends = Double(st_ctimespec.tv_nsec)
#endif
return Date(timeIntervalSince1970: seconds + nanosends / 1_000_000)
}
}
132 changes: 132 additions & 0 deletions Swift/OmFileFormat/Interpolation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
extension OmFileReaderArray where OmType == Float {
/// Read interpolated between 4 points. Assuming dim0 and dim1 are a spatial field
public func readInterpolated(dim0: Int, dim0Fraction: Float, dim1: Int, dim1Fraction: Float) throws -> Float {
let dims = getDimensions()
guard dims.count == 2 else {
throw OmFileFormatSwiftError.requireDimensionsToMatch(required: 2, actual: dims.count)
}
// bound x and y
var dim0 = UInt64(dim0)
var dim0Fraction = dim0Fraction
if dim0+2 > dims[0] {
dim0 = dims[0]-2
dim0Fraction = 1
}
var dim1 = UInt64(dim1)
var dim1Fraction = dim1Fraction
if dim1+2 > dims[1] {
dim1 = dims[1]-2
dim1Fraction = 1
}

// reads 4 points at once
let points = try read(range: [dim0 ..< dim0 + 2, dim1 ..< dim1 + 2])

// interpolate linearly between
return points[0] * (1-dim0Fraction) * (1-dim1Fraction) +
points[1] * (dim0Fraction) * (1-dim1Fraction) +
points[2] * (1-dim0Fraction) * (dim1Fraction) +
points[3] * (dim0Fraction) * (dim1Fraction)
}

/// Read interpolated between 4 points. Assuming dim0 is used for locations and dim1 is a time series
public func readInterpolated(dim0X: Int, dim0XFraction: Float, dim0Y: Int, dim0YFraction: Float, dim0Nx: Int, dim1 dim1Read: Range<UInt64>) throws -> [Float] {
let dims = getDimensions()
guard dims.count == 2 || dims.count == 3 else {
throw OmFileFormatSwiftError.requireDimensionsToMatch(required: 3, actual: dims.count)
}

if dims.count == 2 {
// bound x and y
var dim0X = UInt64(dim0X)
let dim0Nx = UInt64(dim0Nx)
var dim0XFraction = dim0XFraction
if dim0X+2 > dim0Nx {
dim0X = dim0Nx-2
dim0XFraction = 1
}
var dim0Y = UInt64(dim0Y)
var dim0YFraction = dim0YFraction
let dim0Ny = dims[0] / dim0Nx
if dim0Y+2 > dim0Ny {
dim0Y = dim0Ny-2
dim0YFraction = 1
}

// reads 4 points. As 2 points are next to each other, we can read a small row of 2 elements at once
let top = try read(range: [dim0Y * dim0Nx + dim0X ..< dim0Y * dim0Nx + dim0X + 2, dim1Read])
let bottom = try read(range: [(dim0Y + 1) * dim0Nx + dim0X ..< (dim0Y + 1) * dim0Nx + dim0X + 2, dim1Read])

// interpolate linearly between
let nt = dim1Read.count
return zip(zip(top[0..<nt], top[nt..<2*nt]), zip(bottom[0..<nt], bottom[nt..<2*nt])).map {
let ((a,b),(c,d)) = $0
return a * (1-dim0XFraction) * (1-dim0YFraction) +
b * (dim0XFraction) * (1-dim0YFraction) +
c * (1-dim0XFraction) * (dim0YFraction) +
d * (dim0XFraction) * (dim0YFraction)
}
}

// bound x and y
var dim0X = UInt64(dim0X)
let dim0Nx = UInt64(dim0Nx)
var dim0XFraction = dim0XFraction
if dim0X+2 > dim0Nx {
dim0X = dim0Nx-2
dim0XFraction = 1
}
var dim0Y = UInt64(dim0Y)
var dim0YFraction = dim0YFraction
let dim0Ny = dims[0]
if dim0Y+2 > dim0Ny {
dim0Y = dim0Ny-2
dim0YFraction = 1
}

// New 3D files use [y,x,time] and are able to read 2x2xT slices directly
let data = try read(range: [dim0Y ..< dim0Y+2, dim0X ..< dim0X+2, dim1Read])
let nt = dim1Read.count
return zip(zip(data[0..<nt], data[nt..<2*nt]), zip(data[nt*2..<nt*3], data[nt*3..<nt*4])).map {
let ((a,b),(c,d)) = $0
return a * (1-dim0XFraction) * (1-dim0YFraction) +
b * (dim0XFraction) * (1-dim0YFraction) +
c * (1-dim0XFraction) * (dim0YFraction) +
d * (dim0XFraction) * (dim0YFraction)
}
}


/// Read interpolated between 4 points. If one point is NaN, ignore it.
/*public func readInterpolatedIgnoreNaN(dim0X: Int, dim0XFraction: Float, dim0Y: Int, dim0YFraction: Float, dim0Nx: Int, dim1 dim1Read: Range<Int>) throws -> [Float] {

// reads 4 points. As 2 points are next to each other, we can read a small row of 2 elements at once
let top = try read(dim0Slow: dim0Y * dim0Nx + dim0X ..< dim0Y * dim0Nx + dim0X + 2, dim1: dim1Read)
let bottom = try read(dim0Slow: (dim0Y + 1) * dim0Nx + dim0X ..< (dim0Y + 1) * dim0Nx + dim0X + 2, dim1: dim1Read)

// interpolate linearly between
let nt = dim1Read.count
return zip(zip(top[0..<nt], top[nt..<2*nt]), zip(bottom[0..<nt], bottom[nt..<2*nt])).map {
let ((a,b),(c,d)) = $0
var value: Float = 0
var weight: Float = 0
if !a.isNaN {
value += a * (1-dim0XFraction) * (1-dim0YFraction)
weight += (1-dim0XFraction) * (1-dim0YFraction)
}
if !b.isNaN {
value += b * (1-dim0XFraction) * (dim0YFraction)
weight += (1-dim0XFraction) * (dim0YFraction)
}
if !c.isNaN {
value += c * (dim0XFraction) * (1-dim0YFraction)
weight += (dim0XFraction) * (1-dim0YFraction)
}
if !d.isNaN {
value += d * (dim0XFraction) * (dim0YFraction)
weight += (dim0XFraction) * (dim0YFraction)
}
return weight > 0.001 ? value / weight : .nan
}
}*/
}
Loading

0 comments on commit 716b034

Please sign in to comment.